一 目录
不折腾的前端,和咸鱼有什么区别
目录 |
---|
一 目录 |
二 前言 |
三 原生 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,咱们写好了简略版的,让咱们看看简单版须要的条件吧:
- 第一个参数必须是个函数。
const person = new Person()
,然而咱们搞不了原汁原味的,那就变成const person = myNew(Person)
。 - 原型链继承。咱们新建一个对象
obj
,这个obj
的__proto__
指向func
的原型prototype
,即obj.__proto__ === func.prototype
。 - 修改
this
指向。通过apply
绑定obj
和func
的关系,并且将参数作为一个数组传递进去(办法体定义曾经将残余参数解构为数组) - 判断构造函数是否返回
Object
或者Function
。typeof
判断object
须要排除null
,因为typeof null === object
。 - 非函数和对象返回新创建的对象,否则返回构造函数的
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 jsliangconsole.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/ 处取得。