new关键字分析

81 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

new关键字

new关键字让构造函数与普通函数不同。

构造函数

 
 var Person = function(name, age) {
     this.name = name;
     this.age = age;
     this.getName = function() {
         return this.name;
     }
 }
 ​
 var p1 = new Person('Ness', 20);
 console.log(p1.getName());  // Ness
 ​
 console.log(p1 instanceof Person); // true

new关键字做了什么

  1. 声明一个中间对象;
  2. 将该中间对象的原型指向构造函数的原型;
  3. 将构造函数的this,指向该中间对象;
  4. 返回该中间对象,即返回实例对象。
 // 将构造函数以参数的形式传入
 function New(func) {
   // 定义一个中间对象
   let _obj = {};
   // 将该中间对象的原型指向构造函数的原型;
   _obj.__proto__ = func.prototype;
   // 将构造函数的this,指向该中间对象;
   // 使用apply改变this的指向;
   // 使用call,将类数组转化为数组
   // 返回实例
   let ret = func.apply(_obj,Array.prototype.slice.call(arguments, 1));
   // 当我们在构造函数中明确指定了返回对象时,那么new的执行结果就是该返回对象
   if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
       return ret;
   }
   // 返回该中间对象,即返回实例对象。
   return _obj;
 }

检验

 var Person = function(name, age) {
     this.name = name;
     this.age = age;
     this.getName = function() {
         return this.name;
     }
 }
 ​
 var p1 = New(Person,'张三', 12)
 console.log(p1.getName());

call和apply 改变this的指向

call源码实现

  1. 接收一个参数,当前前要成为this指向的对象
  2. 定义一个临时变量,储存当前的this
  3. 收集函数参数,并从第1位截取。因为第0位表示构建函数
  4. 执行当前的this
  5. 删除临时定义的变量。并返回函数值
 // call函数接收一个参数,表示当前要成为this指向的对象
 Function.prototype.call =  function(currentObj = window) {
   // 定义一个临时变量
   let fn;
   // this指向问题,谁调用指向谁。现在是Array.prototype.slice
   currentObj.fn = this;
   // 收集参数
   let args = [...arguments].slice(1);
   // 执行
   const funCall = currentObj.fn(...args);
   // 删除临时变量
   delete currentObj.fn;
   // 返回
   return funCall;
 }

currentObj参数,默认为window。因为在使用中,call的第一个参数有可能回传null。

apply掩码实现

applye的实现和call的实现类似,只不过是接收的参数不同。apply接收一个数组[]。

 Function.prototype.apply =  function(currentObj = window) {
   // 定义一个临时变量
   let fn;
   // this指向问题,谁调用指向谁。现在是Array.prototype.slice
   currentObj.fn = this;
   // 收集参数
   let args = [...arguments].slice(1);
   // 执行
   const funCall = args.length ? currentObj.fn(...args): currentObj.fn();
   // 删除临时变量
   delete currentObj.fn;
   // 返回
   return funCall;
 }

call和apply的区别

call和apply的区别是参数的区别。call接收的是一个一个参数。apply接收的是一个数组。

call 的性能要比 apply 好一些(尤其是传递给函数的参数超过三个的时候)

 let arr = [10, 20, 30],
 ​
 obj = { }
 ​
 function fn (x, y, z) { }
 ​
 fn.apply (obj, arr);
 ​
 fn.call (obj, ...arr);  =》基于 ES6 的展开运算符也可以实现把数组中的每一项一次传递给函数

bind函数

改变this执行,返回一个函数。

  1. 接收一个参数,bind的函数
  2. 保存当前this,谁调用就是谁
  3. 返回一个函数,等待调用
  4. 调用返回的函数,收集参数
  5. 改变this指向,调用。
 Function.prototype.bind = function(context = window) {
   //返回一个绑定this的函数,我们需要在此保存this
   let self = this
   // 可以支持柯里化传参,保存参数
   let arg = [...arguments].slice(1)
   // 返回一个函数
   return function() {
     let newArg = [...arguments];
     return self.apply(context,[...arg,...newArg]);
   }
 }