JavaScript 手写代码 第二期

简介: JavaScript 手写代码 第二期

1.为什么要手写代码?

我们在日常开发过程中,往往都是取出来直接用,从来不思考代码的底层实现逻辑,但当我开始研究一些底层的东西的时候,才开始理解了JavaScript每个方法和函数的底层实现思路,我认为这可以很好的提高我们的代码水平和逻辑思维。

2. 手写代码

2.1 手写实现判断类型函数

2.1.1 前置知识

            let arr = [1, 2, 3];
            let obj = {
                name: 'zs',
                age: 19,
            };
            console.log(typeof null); // object
            console.log(typeof arr); // object
            console.log(typeof obj); // object

因此我们可以知道,当使用 typeof 判断数据类型的时候,无论是判断 null,Array,Object都会输出对象object 类型,所以就无法利用 typeof 方法判断他们的数据类型,所以我们就必须使用其他的方法解决这一个问题,Object.prototype.toString() 方法

Object.prototype.toString()方法介绍

每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString() 方法被每个 Object对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 “[object type]”,其中 type 是对象的类型。


Object.prototype.toString()方法的简单使用

console.log(Object.prototype.toString.call(obj)); // [object Object]
console.log(Object.prototype.toString.call(arr)); // [object Array]
console.log(Object.prototype.toString.call(null)); // [object Null]

可以看出来数组里面的后面就是 我们想要的数据类型,可以利用正则或者splice方法进行截取

2.1.1 手写实现

function getType(obj) {
  let type = typeof obj;
  if (type !== 'object') {
    // 先进行typeof判断,如果是基础数据类型,直接返回
    return type;
  }
  // 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
  return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

2.2 手写实现aplly,call,bind方法

2.2.1 基本使用

1.apply 方法

apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次

1.call 方法

call方法的第一个参数也是this的指向,后面传入的是一个参数列表,跟apply一样,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次

1.bind 方法

bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,改变this指向后不会立即执行,而是返回一个永久改变this指向的函数

2.2.2 实现思路

  1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用call方法等方式调用的情况
  2. 判断传入上下文对象是否存在,如果不存在,则设置为window
  3. 将函数作为上下文对象的一个属性
  1. 判断参数值是否传入
  2. 使用上下文对象来调用这个方法,并保存返回结果
  3. 删除刚才新增的属性
  4. 返回结果

2.2.3 手写实现

手写实现call方法

            let name = 'zs';
            let obj = {
                name: 'lisi',
            };
            function getName(a, b, c) {
                console.log(this.name);
                console.log(a, b, c);
            }
            Function.prototype.myCall = function (context) {
                // 这里的this就是调用的方法
                if (typeof this !== 'function') {
                    throw new Error('type error');
                }
                context = context || window;
                // 截取参数
                let args = [...arguments].slice(1);
                // 在obj对象里面新增调用方法
                context.fn = this;
                // 调用obj里面的方法fn
                let result = context.fn(...args);
                // 删除obj里面新增的方法
                delete context.fn;
                return result;
            };
            getName.myCall(obj, 1, 2, 3);

84d97dac6d0447e88d498f1027a1bcb2.png

正常来说应该输出 ‘zs’ 说明我们通过我们手写的apply方法改变了getName的this指向

手写实现apply方法

只是传递参数的是必须是数组

            Function.prototype.myApply = function (context) {
                // 这里的this就是调用的方法
                if (typeof this !== 'function') {
                    throw new Error('type error');
                }
                context = context || window;
                // 截取参数
                let args = [...arguments].slice(1);
                // 在obj对象里面新增调用方法
                context.fn = this;
                // 调用obj里面的方法fn
                let result = context.fn(args);
                // 删除obj里面新增的方法
                delete context.fn;
                return result;
            };
            getName.myApply(obj, 1, 2, 3);

手写实现bind方法

bind方法实现思路稍有不同

  1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用call等方式调用的情况,
  2. 保存当前函数的引用,获取其余传入参数值
  1. 创建一个函数返回
  2. 函数内部使用 apply 来绑定函数调用,需要判断作为构造函数的情况,这个时候需要传入当前函数的 this 给 apply 调用,其余情况都传入指定的上下文对象

实现代码

            Function.prototype.myBind = function (context) {
                // 判断调用对象是否为函数
                if (typeof this !== 'function') {
                    throw new TypeError('Error');
                }
                // 获取参数
                const args = [...arguments].slice(1),
                    fn = this;
                return function Fn() {
                    // 根据调用方式,传入不同绑定值
                    return fn.apply(this instanceof Fn ? new fn(...arguments) : context, args.concat(...arguments));
                };
            };


目录
相关文章
|
5天前
|
资源调度 前端开发 JavaScript
Babel:JavaScript代码的编译利器
Babel:JavaScript代码的编译利器
|
5天前
|
JavaScript 前端开发 安全
抽象语法树(AST):理解JavaScript代码的抽象语法树
抽象语法树(AST):理解JavaScript代码的抽象语法树
|
6天前
|
JSON JavaScript 前端开发
JavaScript原生代码处理JSON的一些高频次方法合集
JavaScript原生代码处理JSON的一些高频次方法合集
|
6天前
|
存储 JavaScript 前端开发
非常实用的JavaScript一行代码(整理总结)
非常实用的JavaScript一行代码(整理总结)
34 0
|
6天前
|
JavaScript 前端开发 测试技术
如何编写JavaScript模块化代码
如何编写JavaScript模块化代码
14 0
|
5天前
|
存储 JavaScript 前端开发
掌握值类型和引用类型,让你的JavaScript代码更上一层楼!
掌握值类型和引用类型,让你的JavaScript代码更上一层楼!
|
6天前
|
JavaScript 前端开发 测试技术
编写JavaScript模块化代码主要涉及将代码分割成不同的文件或模块,每个模块负责处理特定的功能或任务
【5月更文挑战第10天】编写JavaScript模块化代码最佳实践:使用ES6模块或CommonJS(Node.js),组织逻辑相关模块,避免全局变量,封装细节。利用命名空间和目录结构,借助Webpack处理浏览器环境的模块。编写文档和注释,编写单元测试以确保代码质量。通过这些方法提升代码的可读性和可维护性。
13 3
|
6天前
|
JavaScript 前端开发 开发工具
【JavaScript 与 TypeScript 技术专栏】TypeScript 如何提升 JavaScript 代码的可读性与可维护性
【4月更文挑战第30天】TypeScript 提升 JavaScript 代码的可读性和可维护性,主要通过静态类型系统、增强代码组织、智能提示与错误检测、文档化和在大型项目中的优势。静态类型减少误解,类和接口提供结构,智能提示提高编码效率,类型注解充当内置文档。在大型项目中,TypeScript 降低理解差异,平滑迁移现有 JavaScript 项目,助力提高开发效率和项目质量。
|
6天前
|
JavaScript 前端开发 算法
< JavaScript小技巧:如何优雅的用【一行代码 】实现Js中的常用功能 >
在开发中,采用简洁的语法和结构,遵循一致的命名规范,具有良好的代码组织和注释,能很好的提高代码的质量。可读性:易于阅读和理解。清晰的命名、简洁的语法和良好的代码结构可以使代码的意图更加明确,降低理解代码的难度,提高代码的可读性。可维护性:易于维护。当代码逻辑清晰、结构简洁时,开发者可以更快速地定位和修复bug,进行功能扩展或修改。同时,可读性高的代码也有助于后续的代码重构和优化。可扩展性:更具有扩展性和灵活性。清晰的代码结构和简洁的代码风格使得添加新功能、修改现有功能或扩展代码更加容易。
< JavaScript小技巧:如何优雅的用【一行代码 】实现Js中的常用功能 >
|
6天前
|
前端开发 JavaScript 容器
JavaScript、CSS像素动画特效代码
此示例创建一个带有像素粒子的容器,每隔300毫秒就会动态添加一个新的像素粒子,然后通过CSS的关键帧动画(`@keyframes`)使它们产生上升和逐渐消失的动画效果。你可以根据需要修改像素粒子的颜色、大小、动画效果和创建速度。
14 0