实现Javascript的new

一、new关键字介绍

在js里声明对象的方式有好几种,但是每一种方案的底层就是对Object根对象进行一个继承来实现。为了让大家使用的方便,js提供了new关键字,具体的使用方法为var 变量名 = new 方法名(参数),实例代码如下:

function Person(name) {
    this.name = name;
    
    this.sayName = function() {
        console.log(this.name);
    }
}

var person = new Person('张三');

拥有面向对象编程经验的同学看这段代码会有两个疑问,为什么js声明对象的方式是对function使用new,而不是class使用?方法名大写和小写有区别吗?

  1. js中所有的对象都继承于Object,这里的对象和Java等静态编程语言的对象有区别。在js中一切皆为对象,function也是对象的一种(见MDN的Function)。new关键字的本质就是一个语法糖,封装了继承Object以及其他的一些操作,这样会方便开发者声明对象。
  2. 在js中是区分大小写的,当function会用来声明对象时,就会使用大驼峰来命名。说的通俗一些,是为了遵守编程规范。

二、new做了哪些事

MDN的new运算符一章中提到的new的过程大致可以分成以下四步:

  1. 创建一个空的简单JavaScript对象(即{});
  2. 链接该对象(即设置该对象的构造函数)到另一个对象;
  3. 将步骤1新创建的对象作为this的上下文;
  4. 如果该函数没有返回对象,则返回this。

对于以上的过程,有些地方会存在一些疑问。使用构造方法实例化的对象和构造方法有什么关系?对象的属性会在原型上吗?如果构造方法返回了Number、String等基础类型的值怎么样?

不妨写代码测试一下。

function Person(name) {
    this.name = name;
}

function Person1(name) {
    this.name = name;
    return {};
}

function Person2(name) {
    this.name = name;
    return '李四';
}
var person = new Person('张三');
var person = new Person1('张三');
var person = new Person2('张三');

console.log(person); // {name: '张三'}
console.log(person1); // {}
console.log(person2); //  {name: '张三'}
console.log(person == Person); // false
console.log(person.name); // 张三
console.log(person.hasOwnProperty('name')); // true
console.log(person instanceof Person); // true

根据打印的结果可以看出:

  1. 如果返回的是基础类型,和不返回值是一样的,都会成功实例化Person对象。否则以返回的对象实例化。
  2. personPerson不相等,说明对象的引用变量,那么new关键字就会返回一个新对象。
  3. 通过person.name能获取到name的值,而在Person内部,需要通过this的方法获取,在很多的博客上是说this指向了返回的对象,其实这个说法不对。《JavaScript高级程序设计》一书解释是执行了构造函数中的代码,这里的构造函数就是指的Person方法。
  4. name是person本身的属性,而不是原型上的。
  5. instanceof关键字判断出person是Person的实例。

三、开始写一个new

1. 简单版

按照MDN给的过程来实现一下。声明一个空对象,然后执行构造方法,再将对象的prototype指向对象的__proto__属性,最后返回这个对象。

function myNew(object) {
    var o = {};
    object.apply(o);
    o.__proto__ = object.prototype;
    return o;
}

简单的new就完成了,但是有三个问题是没有考虑到的:

  • 构造函数有参数的情况;
  • 构造函数第一个参数并不一定是一个方法;
  • 构造函数可能会有返回值;

2. 进阶版

针对上面说到的问题进行了一些改进,代码如下:

function myNew(object) {
    if (typeof object !== 'function') {
        throw new TypeError(object + ' is not a constructor');
    }
    var o = {};
    var args = Array.prototype.slice.call(arguments, 1);
    var result = object.apply(o, args);

    if (typeof result === 'object') {
        return result;
    }
    o.__proto__ = object.prototype;
    return o;
}

function Person(name) {
    this.name = name;
    
    this.sayName = function() {
        console.log(this.name);
    }
}

var np = myNew(Person, '张三');
var np2 = myNew(Person, '李四');

四、总结

实现new的代码并不多,但是在过程中,值得让人关注的地方其实有很多。

  1. 构造方法中有prototype属性,但是在实例化之后,对象里是没有prototype属性的,取而代之的是有__proto__属性,两者的值是一样的。
  2. 既然是由构造方法实例化而来,那关键字instanceof是如何判断对象是构造方法的实例呢?没错,是根据constructor.prototype来判断的。但是在实现的过程中没有写过constructor相关的代码,答案就在object.apply(o, args)里了。
  3. 声明空对象使用的是{},还有别的方式声明空对象吗?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值