一文搞懂new(真)

289 阅读4分钟

new的作用及其手写

new的原理和想法其实很简单,new帮我们完成创建空对象,绑定目标对象的原型等操作.也就是说通过 new 操作符,实例与构造函数通过原型链连接了起来

就这样

function newF() {
  let obj =new Object()//{};
  let Constructor = [].shift.call(arguments);
  obj.__proto__=Constructor.prototype;
  let result =Constructor.apply(obj, arguments);
  return typeof result ==='object'? result : obj 
}

哎哎先别急着关

这代码第一次确实不是给人看的,或许第二次也不是,没事我们一行一行的看

function newF() {
  let obj = {}; // 创建一个新的对象
  // 取出第一个参数,该参数就是我们将会传入的构造函数,比如在调用new(P)的时候,Constructor就是P本身
  // arguments(传入的参数)会被shift去除第一个参数,剩余的就是构造器P的参数
  let Constructor = [].shift.call(arguments);(1// 将obj的原型指向构造函数,此时obj可以访问构造函数原型中的属性
  obj.__proto__ = Constructor.prototype;(2// 改变构造函数的this的指向,使其指向obj, 此时obj也可以访问构造函数中的属性了
  let result = Constructor.apply(obj, arguments);(3// 确保 new 出来的是个对象 返回的值是什么就return什么
  return typeof result === 'object' ? result : obj (4)
}

还是一脸懵?我们在接着分析

  • 1·arguments?

arguments 是一个类数组对象。代表传给一个function的参数列表,什么是类数组,简单理解就是具有数组的某些特性,但是不具有数组的api,我们在这里接收的参数中,第一个参数是我们的构造函数,就类似new Date()中的date(),但他并不是函数执行需要的参数,所以我们在这里将他去除。那可能有人会说你直接shift()不就好啦,还记得我刚刚提到的吗,类数组不具有数组的api,所以这里我们调用了[]的数组方法 Array.prototype.slice.call(arguments);这才是将arguments转化为组数的方法^

  • 2·拿——proto——和propotype赋值是几个意思

这个很好理解,因为我们每个实例对象的隐式原型——proto——都是指向创建他的构造函数的,这里我们要帮他指一下

  • 3·不是讲new吗?apply是干嘛的

首先说一下作用,新对象本身没有构造函数的私有变量和方法,所以这里借用apply(1)改变this指向,执行构造函数的方法

  • 4·为什么返回值还要三元判断

这个简单,因为构造函数会返回一个对象,所以在这里我们要判断一下

怎么实现new的过程中又加入了apply?

这是一个问题,因为在讲解一个概念又加入新的概念是很差的体验,但是删不得,那apply能干嘛呢

	假如我们现在有一个
    var person1 = {firstName: "Bill",lastName: "Gates",}
    又有一个
    var person = {
    fullName: function() {return this.firstName + " " + this.lastName;}}
    我们想取到person1的值该怎么办

很简单

person.fullName.apply(person1);  // 将返回 "Bill Gates"

这就是apply的作用,改变this的指向,像他这么厉害的还有2个call和bind,他们实现的功能都差不多,这里我们尝试实现一下apply

    Function.prototype.myApply = function (context, args) {
    //这里默认不传就是给window,也可以用es6给参数设置默认参数
    context = context || window
    args = args ? args : []
    //给context新增一个独一无二的属性以免覆盖原有属性
    const key = Symbol()
    context[key] = this //这里的this指向执行apply方法的对象本身,
    //将自身赋值给传入对象的属性上,这样就可以执行传入对象的方法了
    //通过隐式绑定的方式调用函数
    const result = context[key](...args) //传参并执行
    //需要对象本身在context的作用域里执对应的方法
    //删除添加的属性
    delete context[key]
    //返回函数调用的返回值
    return resul
    }

结束了

并没有 我们做了一顿饭,但是还没吃

下面尝一下


function P(firstName, lastName) {
  this.age=10;
  this.getName=function() {
    return`${firstName}${lastName}`;
  };
}
function newF() {
  let obj =new Object();
  let Constructor = [].shift.call(arguments);
  obj.__proto__=Constructor.prototype;
  let result =Constructor.apply(obj, arguments);
  return typeof result ==='object'? result : obj 
}
 
let p =newF(P, 'amanda', 'kelake');
p.getName();
// "amanda kelake"

真香

本文参考了至少以下文章,如有纰漏请帮忙指出^^

进击的前端面试

JavaScript arguments 对象详解

JavaScript 函数 Apply

JS 中的 call、apply、bind 方法详解

深入JavaScript继承原理