关于前端:jsliang-求职系列-11-手写-new

40次阅读

共计 3016 个字符,预计需要花费 8 分钟才能阅读完成。

一 目录

不折腾的前端,和咸鱼有什么区别

目录
一 目录
二 前言
三 原生 new
四 手写 new
 4.1 简略实现
 4.2 欠缺版本

<!– 目录完结 –>

二 前言

返回目录

面试官:来手写一个 new

看到这道题,不要急不要慌,jsliang 逐渐深刻带你搞一个。

咱们先看一个案例:

this.name = 'jsliang';
let Foo = function() {this.name = 'zhazhaliang';}
let foo = new Foo();
console.log(foo.name); // 输入啥?console.log(window.name); // 输入啥?

下面代码输入啥?


答案是:

  • zhazhaliang
  • jsliang

那么,这道题中的 new 做了啥呢?咱们深入研究钻研。

三 原生 new

返回目录

先看一下原生 new 的一个案例,思考下 new 能做啥:

function Person(name, age){
  this.name = name;
  this.age = age;
 
  // return;                              // 返回 this
  // return null;                         // 返回 this
  // return this;                         // 返回 this
  // return false;                        // 返回 this
  // return 'hello world';                // 返回 this
  // return 2;                            // 返回 this
  
  // return [];                           // 返回 新建的 [], person.name = undefined
  // return function(){};                 // 返回 新建的 function,摈弃 this, person.name = undefined
  // return new Boolean(false);           // 返回 新建的 boolean,摈弃 this, person.name = undefined
  // return new String('hello world');    // 返回 新建的 string,摈弃 this, person.name = undefined
  // return new Number(32);               // 返回 新的 number,摈弃 this, person.name = undefined
}

var person = new Person("jsliang", 25);
console.log(person); // Person {name: "jsliang", age: 25}

四 手写 new

返回目录

那么咱们开始了解 new 外面的内容,看看怎么手写一个 new

4.1 简略实现

返回目录

那么咱们先简略地用 3 行代码写一个 new 试试:

  • 首先创立一个空对象 tempObj = {}
  • 接着调用 Foo.apply 办法,将 tempObj 作为 apply 办法的参数,这样当 Foo 的执行上下文创立时,它的 this 就指向 tempObj 对象。
  • 而后执行 Foo 函数,此时的 Foo 函数执行上下文中的 this 指向了 tempObj 对象。
  • 最初返回 tempObj 对象。
function myNew(func, ...args) {const tempObj = {};
  func.apply(tempObj, args);
  return tempObj;
}

this.name = 'jsliang';
let Foo = function(name, age) {
  this.name = name;
  this.age = age;
}
let foo = myNew(Foo, 'zhazhaliang', 25);
console.log(foo.name); // 输入啥?console.log(foo.age); // 输入啥?console.log(window.name); // 输入啥?

如上,咱们能够看到此时 this 是属于 tempObj 的,绑定到 foo 下来了,从而获取到:

  • zhazhaliang
  • 25
  • jsliang

wow,是不是恍然大悟,简略 new 的实现只须要 3 行代码!

4.2 欠缺版本

返回目录

OK,咱们写好了简略版的,让咱们看看简单版须要的条件吧:

  1. 第一个参数必须是个函数 const person = new Person(),然而咱们搞不了原汁原味的,那就变成 const person = myNew(Person)
  2. 原型链继承 。咱们新建一个对象 obj,这个 obj__proto__ 指向 func 的原型 prototype,即 obj.__proto__ === func.prototype
  3. 修改 this 指向 。通过 apply 绑定 objfunc 的关系,并且将参数作为一个数组传递进去(办法体定义曾经将残余参数解构为数组)
  4. 判断构造函数是否返回 Object 或者 Functiontypeof 判断 object 须要排除 null,因为 typeof null === object
  5. 非函数和对象返回新创建的对象,否则返回构造函数的 return

上面贴一下最终实现:

function myNew(func, ...args) {
  // 1. 判断办法体
  if (typeof func !== 'function') {throw '第一个参数必须是办法体';}

  // 2. 创立新对象
  const obj = {};

  // 3. 这个对象的 __proto__ 指向 func 这个类的原型对象
  // 即实例能够拜访构造函数原型(constructor.prototype)所在原型链上的属性
  obj.__proto__ = Object.create(func.prototype);

  // 为了兼容 IE 能够让步骤 2 和 步骤 3 合并
  // const obj = Object.create(func.prototype);

  // 4. 通过 apply 绑定 this 执行并且获取运行后的后果
  let result = func.apply(obj, args);
  
  // 5. 如果构造函数返回的后果是援用数据类型,则返回运行后的后果
  // 否则返回新创建的 obj
  const isObject = typeof result === 'object' && result !== null;
  const isFunction = typeof result === 'function';
  return isObject || isFunction ? result : obj;
}

// 测试
function Person(name) {
  this.name = name;
  return function() { // 用来测试第 5 点
    console.log('返回援用数据类型');
  };
}
// 用来测试第 2 点和第 3 点
Person.prototype.sayName = function() {console.log(`My name is ${this.name}`);
}
const me = myNew(Person, 'jsliang'); // 用来测试第 4 点
me.sayName(); // My name is jsliang
console.log(me); // Person {name: 'jsliang'}

// 用来测试第 1 点
// const you = myNew({name: 'jsliang'}, 'jsliang'); // 报错:第一个参数必须是办法体 

这样,咱们就理解 new 是啥东东,碰到手写 new 的时候就不慌啦!


jsliang 的文档库由 梁峻荣 采纳 常识共享 署名 - 非商业性应用 - 雷同形式共享 4.0 国内 许可协定 进行许可。<br/> 基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/> 本许可协定受权之外的应用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处取得。

正文完
 0