Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

手动实现 new 关键字 #40

Open
18888628835 opened this issue Jun 9, 2021 · 0 comments
Open

手动实现 new 关键字 #40

18888628835 opened this issue Jun 9, 2021 · 0 comments

Comments

@18888628835
Copy link
Owner

18888628835 commented Jun 9, 2021

手动实现 new 关键字

new 做了哪些事

在实现 new 关键字之前我们需要了解 new 到底做了什么工作?

我们从一段简单的代码入手来看

function fn(name){
  this.name=name
}
fn.prototype.say=function (){
  alert(this.name)
}
const boy=new fn('qiuyanxi')

这段代码非常简单,它定义了一个构造函数,然后给了这个构造函数的原型一个名叫 say 的函数。

毫无疑问,当我调用 boy.say的时候会打印qiuyanxi,那么我们推断 new 做了什么?

  • 首先一定会返回一个对象。
  • 该对象内肯定有个 name 属性,值为 qiuyanxi
  • 既然这个对象可以调用 say 方法,说明也继承了 say 方法。

为了证明我们的猜想,我们从打印台查看一下boy 这个对象
image

推断正确,有一点值得我们注意,say 方法是在 boy.__poroto__上的,而这个属性有个constructor属性指向 fn,那么我们就知道这是 fn 函数的原型。

fn.prototype===boy.__proto__
// true

手写 new 功能

知道了原理,那我们可以开始动手了

1.我们需要先定义一个构造函数

function create(name){
  this.name=name
}
create.prototype.say=function (){
  alert(this.name)
}

2.创建一个 new 函数,接收构造函数为参数

function lineNew(func){
  const x=Object.create(func.prototype)  //创建一个空对象,原型连接到传入的构造函数身上
  return x //返回一个对象
}

3.既然内部需要有 name 属性,那我们就需要把 name 传进去,而且需要调用构造函数

function likeNew(func,...args){
   const x=Object.create(func.prototype)
   func.call(x,...args)
  return x
}

实验一下:

const boy=likeNew(create,'qiuyanxi')

image

看起来没问题。

细节

虽然功能实现了,但是有些细节问题需要处理。

比如说箭头函数是没有 原型的,所以我们可以手动帮它创建一个。

比如说会不会有些人不传函数进去呀?所以我们需要做一点 Polyfill

优化代码

function likeNew(func,...args){
  if(typeof func !=='function'){
    throw new Error('first arg is not function')
  }
  let x={}
  if(func.prototype){
    x=Object.create(func.prototype)
  }else{
    x.__proto__={constructor:func} //手动创建一个原型接上
  }
   func.call(x,...args)
  return x
}

构造函数返回对象问题

有时候面试题会问你构造函数返回一个对象,那么 new会怎么做。

我们直接实验出真知

function foo(name){
  this.name=name;
  return {age:18}
}
const boy=new foo('qiuyanxi')
boy // {age: 18}

结论:如果构造函数返回了对象,那new就返回这个对象,否则就返回new自己创建的对象。

这一点MDN 也有介绍

当代码 new Foo(...) 执行时,会发生以下事情:

1.一个继承自 Foo.prototype 的新对象被创建。
2.使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
3.由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)

那么我们继续改一下代码

function likeNew(func,...args){
  if(typeof func !=='function'){
    throw new Error('first arg is not function')
  }
  let x={}
  if(func.prototype){
    x=Object.create(func.prototype)
  }else{
    x.__proto__={constructor:func} //手动创建一个原型接上
  }
  return typeof func.call(x,...args)==='object'?func.call(x,...args):x
}

一般面试不需要做 Polyfill,这里附上简单的几行代码

      function _new(initFunc, ...rest) {
        // let xx.__proto__ = initFunc.prototype
        let cache = Object.create(initFunc.prototype);
        let result = initFunc.call(cache, ...rest);
        return typeof result === "object" ? result : cache;
      }

结束~

enjoy!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant