5分钟快速手写实现:new

1,615 阅读5分钟

前言:

以下记录模拟实现new方法,本文将以简单且清晰逻辑的带你一步步理解如何手写new函数。

首先抛出最终版本的new函数,如果有不明白的请你一定随我看下去!一定有收获!( ఠൠఠ )ノ!!!

function myNew(Fn) {
    if (typeof Fn !== 'function') throw new TypeError('This is not a constructor') // Fn校验
    var args = Array.from(arguments).slice(1) // 取入参
    var obj = {} // 1.创建一个空的简单JavaScript对象(即`  {}  `)
    obj.__proto__ = Fn.prototype // 2.  为步骤1新创建的对象添加属性`  __proto__  `,将该属性链接至构造函数的原型对象
    var res = Fn.call(obj, ...args) // 3.  将步骤1新创建的对象作为this的上下文并传入参数;
    return Object(res) === res ? res : obj // 4.  如果该函数没有返回对象,则返回this。
}

new

一句话概括:实例化构造函数。 new 关键字会进行如下的操作:

  1. 创建一个空的简单JavaScript对象(即 {} );
  2. 为步骤1新创建的对象添加属性 __proto__ ,将该属性链接至构造函数的原型对象 ;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this。 以上4点是MDN上关于new的描述,可以按照以上4步去写

1. 第一版本new

function myNew1(Fn) {
    var obj = {} // 1.创建一个空的简单JavaScript对象(即`  {}  `)
    obj.__proto__ = Fn.prototype // 2.  为步骤1新创建的对象添加属性`  __proto__  `,将该属性链接至构造函数的原型对象 ;
    var res = Fn.call(obj) // 3.  将步骤1新创建的对象作为this的上下文 ;
    return res ? res : obj // 4.  如果该函数没有返回对象,则返回this。
}

var Fn = function(name, age) {
    this.name = name
    this.age = age
}
Fn.prototype.say = function() {
    console.log('Fn.prototype.say')
}

// 测试

var newObj = myNew1(Fn)
newObj // Fn {name: undefined, age: undefined}
newObj.say() // Fn.prototype.say

var newObj1 = new Fn()
newObj1 // Fn {name: undefined, age: undefined}
newObj1.say() // Fn.prototype.say

好了,本文结束!

balckmansmall.jpg

然而事情貌似没有这么简单😂

  1. new可以传参
  2. Fn函数校验问题
  3. 如果Fn函数内有返回又会怎样

2. 第二版本new

问题一:
function myNew2(Fn) {
    var args = Array.from(arguments).slice(1) // 取入参
    var obj = {} // 1.创建一个空的简单JavaScript对象(即`  {}  `)
    obj.__proto__ = Fn.prototype // 2.  为步骤1新创建的对象添加属性`  __proto__  `,将该属性链接至构造函数的原型对象
    var res = Fn.call(obj, ...args) // 3.  将步骤1新创建的对象作为this的上下文并传入参数;
    return res // 4.  如果该函数没有返回对象,则返回this。
}

var Fn = function(name, age) {
    this.name = name
    this.age = age
}
Fn.prototype.say = function() {
    console.log('Fn.prototype.say')
}

// 测试
var newObj = myNew2(Fn, 'Leo', 18)
newObj // Fn {name: 'Leo', age: 18}
newObj.say() // Fn.prototype.say

var newObj1 = new Fn('Leo', 18)
newObj1 // Fn {name: 'Leo', age: 18}
newObj1.say() // Fn.prototype.say

问题二:

function myNew2(Fn) {
    if (typeof Fn !== 'function') throw new TypeError('this is not a constructor')
    var args = Array.from(arguments).slice(1) // 取入参
    var obj = {} // 1.创建一个空的简单JavaScript对象(即`  {}  `)
    obj.__proto__ = Fn.prototype // 2.  为步骤1新创建的对象添加属性`  __proto__  `,将该属性链接至构造函数的原型对象
    var res = Fn.call(obj, ...args) // 3.  将步骤1新创建的对象作为this的上下文并传入参数;
    return res // 4.  如果该函数没有返回对象,则返回this。
}

// 测试
var newObj = myNew2(1, 'Leo', 18) // uncaught TypeError: this is not a constructor

var Fn = 1
var newObj1 = new Fn('Leo', 18) // Uncaught TypeError: Fn is not a constructor

问题三:

function myNew2(Fn) {
    if (typeof Fn !== 'function') throw new TypeError('this is not a constructor')
    var args = Array.from(arguments).slice(1) // 取入参
    var obj = {} // 1.创建一个空的简单JavaScript对象(即`  {}  `)
    obj.__proto__ = Fn.prototype // 2.  为步骤1新创建的对象添加属性`  __proto__  `,将该属性链接至构造函数的原型对象
    var res = Fn.call(obj, ...args) // 3.  将步骤1新创建的对象作为this的上下文并传入参数;
    return Object(res) === res ? res : obj // 4.  如果该函数没有返回对象,则返回this。
}

var Fn = function(name, age) {
    this.name = name
    this.age = age
    return 1 // 这里返回的是非对象(null/undefined/number/string/boolen)
}
Fn.prototype.say = function() {
    console.log('Fn.prototype.say')
}

// 测试
var newObj = myNew2(Fn, 'Leo', 18)
newObj // Fn {name: 'Leo', age: 18}
newObj.say() // Fn.prototype.say

var newObj1 = new Fn('Leo', 18)
newObj1 // Fn {name: 'Leo', age: 18}
newObj1.say() // Fn.prototype.say


var Fn = function(name, age) {
    this.name = name
    this.age = age
    return {} // 注意这里返回了对象
}
Fn.prototype.say = function() {
    console.log('Fn.prototype.say')
}

// 测试

var newObj = myNew2(Fn, 'Leo', 18)
newObj // Fn {}
newObj.say() // Uncaught TypeError: newObj.say is not a function

var newObj1 = new Fn('Leo', 18)
newObj1 // Fn {}
newObj1.say() // Uncaught TypeError: newObj.say is not a function

var Fn = function(name, age) {
    this.name = name
    this.age = age
    return function cbFn(){} // 注意这里返回了函数(本质上也是对象,不理解的可以去复习一下对象类型)
}
Fn.prototype.say = function() {
    console.log('Fn.prototype.say')
}

// 测试
var newObj = myNew2(Fn, 'Leo', 18)
newObj // ƒ cbFn(){}
newObj.say() // Uncaught TypeError: newObj.say is not a function

var newObj1 = new Fn('Leo', 18)
newObj1 // ƒ cbFn(){}
newObj1.say() // Uncaught TypeError: newObj.say is not a function

至此对于new的模拟告一段落

最终输出结果:

function myNew(Fn) {
    if (typeof Fn !== 'function') throw new TypeError('This is not a constructor') // Fn校验
    var args = Array.from(arguments).slice(1) // 取入参
    var obj = {} // 1.创建一个空的简单JavaScript对象(即`  {}  `)
    obj.__proto__ = Fn.prototype // 2.  为步骤1新创建的对象添加属性`  __proto__  `,将该属性链接至构造函数的原型对象
    var res = Fn.call(obj, ...args) // 3.  将步骤1新创建的对象作为this的上下文并传入参数;
    return Object(res) === res ? res : obj // 4.  如果该函数没有返回对象,则返回this。
}

相关知识:

  • apply/call
  • 对象类型相关
  • 原型链相关

相关文章推荐

最后

原创不易希望大家多多支持,欢迎拍砖!!~ o( ̄▽ ̄)o