JavaScript进阶开发:模拟new Object()过程

2019-02-2421:13:24WEB前端开发Comments2,365 views字数 3232阅读模式

JavaScript新建对象的过程

在ES5中定义一个函数来创建对象,如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    return name;
}
var person = new Person("xuan");
console.log(person.name);//输出:xuan
console.log(person.getName());//输出:xuan
复制代码

我们看到当我们新建一个对象,我们就可以访问构造器中的指向this的属性,还可以访问原型中的属性。我们不妨把JavaScript调用new的过程主要由下面四步组成:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

  1. 新生成一个空对象
  2. 将空对象链接到原型中
  3. 绑定this
  4. 返回新对象

下面跟着我按照这个思路来创建对象:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

function create(){
    //Todo
}
person = create(Person,"xuan");//create(ObjectName,...arguments)
复制代码

我们使用如上所示的函数来模拟new关键字。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

首先第一步新建一个对象:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

function create(){
    var obj = new Object();
    return obj;
}
person = create(Person,"xuan");
复制代码

现在已经创建并返回一个对象,当然现在打印出来肯定是一个普通的对象,毕竟流程还没有走完,我们接着往下看。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

第二步链接到原型中:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

function create(){
    var obj = new Object();
    var constructor = [].shift.call(arguments);
    console.log(constructor);
    console.log(arguments);
    obj.__proto__ = constructor.prototype;
    return obj;
}

person = create(Person,"xuan");
复制代码
JavaScript进阶开发:模拟new Object()过程

现在把构造函数和参数都打印出来了。没问题!文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

第三步绑定this,如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

function create() {
  let obj = new Object();
  let constructor = [].shift.call(arguments)
  obj.__proto__ = constructor.prototype
  constructor.apply(obj, arguments);
  console.log(obj);  
  return obj;
}
person = create(Person,"xuan");
复制代码
JavaScript进阶开发:模拟new Object()过程

打印结果实现new对象的效果。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

现在改一下构造函数代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

function Person(name){
    this.name = name;
    return {
        name:"abc"
    }
}
var person = new Person("xuan");
console.log(person);
console.log(Object.prototype.toString.call(person));
复制代码

效果如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

JavaScript进阶开发:模拟new Object()过程

我们执行一下我们构建的函数效果如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

JavaScript进阶开发:模拟new Object()过程

发现不一致,所以我们要处理第三步绑定this中apply函数的返回值:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

function create() {
  let obj = new Object();
  let constructor = [].shift.call(arguments)
  obj.__proto__ = constructor.prototype
  //constructor.apply(obj, arguments);
  let res = constructor.apply(obj, arguments);
  if(res){
     return res;
  }else{
     return obj;
  }
}
person = create(Person,"xuan");
复制代码

效果如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

JavaScript进阶开发:模拟new Object()过程

完美!现在我们思考一下这里的res返回值有三种情况:undefined,基本类型,对象。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

如果res是undefined时,返回obj;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

如果res是基本类型我们也返回obj;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

如果res是对象我们返回res对象;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

综合一下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

如果返回的res对象是Object类型那么返回res,否则返回obj。当然其他的判断条件也是可以的。最后代码优化如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

function create() {
  let obj = new Object();
  let constructor = [].shift.call(arguments)
  obj.__proto__ = constructor.prototype
  //constructor.apply(obj, arguments);
  let res = constructor.apply(obj, arguments);
  return res instanceof Object?res:obj;
}
person = create(Person,"xuan");
复制代码

几个问题

现在的代码已经完美了么?我们先来提几个问题。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

  1. new Object()创建的对象纯净么?
  2. 为啥使用[].shift.call()来进行参数分割?arguments是一个数组么?

new Object()创建的对象纯净么?

首先什么是纯净?我们定义一个对象的__proto__属性为空的对象是一个纯净的对象。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

在第二步的时候中已经改变的obj的原型链,所以无论它前面的原型链是咋样的都无所谓,但是为了保证对象的纯净性,我们有必要引出Object.create(),该方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。我们来看一下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

var person1 = Object.create({});
复制代码

打印如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

JavaScript进阶开发:模拟new Object()过程

我们看到person1的__proto__指向了{}对象,所以我们在上述代码中直接修改如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

function create() {
  let constructor = [].shift.call(arguments);
  let obj = Object.create(constructor.prototype);
  let res = constructor.apply(obj, arguments);
  return res instanceof Object?res:obj;
}
person = create(Person,"xuan");
复制代码

为啥使用[].shift.call()来进行参数分割?arguments是一个数组么?

首先我们知道arguments是函数传入的参数,那么这个参数是数组么?我们打印一下便知:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

console.log(arguments);
console.log(Object.prototype.toString.call(arguments));
console.log(arguments instanceof Array);
复制代码

结果如下文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

JavaScript进阶开发:模拟new Object()过程

不是数组。我们展开发现他跟数组很像,查一下资料发现这个对象是类数组。里面没有shift函数,直接调用shift会报错。我们使用使用Array.from(arguments)将arguments转成数组,然后在调用shift函数也是一种思路。但是在这里我们使用apply最适合。所以下述代码是模拟new Object()的最优代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

function create() {
  let constructor = [].shift.call(arguments);
  let obj = Object.create(constructor.prototype);
  let res = constructor.apply(obj, arguments);
  return res instanceof Object?res:obj;
}
person = create(Person,"xuan");

作者:EdwardXuan
链接:https://juejin.im/post/5c6ed96ae51d450da463513c
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html

文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/9680.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/9680.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定