lovelmh13/myBlog

关于new需要知道的事

Opened this issue · 0 comments

JavaScript中的new要区别于传统的面向对象语言的new。

我们要明确一下,在JavaScript中,构造函数是什么?

在JavaScript中的构造函数,只是写在new操作符后面的一个普通的函数,并不属于某个类,更不会实例化一个类。“构造函数“ 这个说法更确切的应该成为 “构造调用” 。后面为了习惯说法,还是称为构造函数。

new的时候,发生了什么呢?

new会劫持所有普通函数并用构造对象的形式来调用它。

function Foo() {
	console.log('rookie dog');
}
let a = new Foo();	// rookie dog
a; // {}

new通过构造函数调用,创建了的构造函数的“实例”,使其含有构造函数的属性,并且__proto__可以访问到造函数原型链中的属性:

  • 内部创建一个新的空对象
  • 把新对象的__proto__与构造函数的prototype联系上
  • 把新对象this绑定到构造函数上成为新的非空对象
  • 判断返回的新对象是简单类型还是复杂类型,复杂类型的话要返回构造函数本身(用过apply指向构造函数的那个对象),简单类型返回新的对象(__proto__与构造函数的prototype联系的对象)
    • (因为new 的时候就是这个么返回的,实例本来就是希望继承构造函数的属性,含有构造函数的原型的,并不希望得到的是构造函数的返回值)

让我们手动实现一个new

function create(Con, ...args) {
    let obj = {};
    obj.__proto__ = Con.prototype;
    let result = Con.apply(obj, args);

    if (result instanceof Object) {
        return result;
    } else {
        return obj;
    }
}

function Test(name, age) {
    this.name = name
    this.age = age
}
Test.prototype.sayName = function () {
    console.log(this.name)
}
const a = create(Test, 'rookie dog', 23);
console.log(a.name) // 'rookie dog'
console.log(a.age)  // 23
a.sayName()         // 'lmh'

注意事项

让我们看到上面new过程的第二点并联系上面的代码:将a内部的[[prototype]]连接到Test.prototype所指的对象。

在传统的面向对象的语言中,类可以被复制。但是在JavaScript中,并没有这种复制,而是通过[[prototype]]进行关联。如果要进行复制,需要使用Object.create()

Test.prototype.sayName = function () {
    console.log(this.name)
}

let a = new Test('rookie dog', 23)
a.sayName() // rookie dog

Test.prototype.sayName = function () {
    console.log(1)
}
a.sayName() // 1 --> 这是关联,而不是复制

当然,如果你改变了asayName,实际上是在a上面新加了一个sayName方法,这并不会影响到Test,因为sayName本是通过[[prototype]]来调用到的