Site Overlay

JS原型系统

在JavaScript 中关于原型系统的名词:prototype__proto__,原型链。乍一看简直一头雾水,这也和js的继承机制的设计有所关联。

JS继承机制

在js中的继承机制和JAVA或者C语言不一样,它没有类(class)的概念。这是因为js最初的设计只是为了做一些简单的工作,例如校验表单,但是js中都是对象,需要有一种机制将他们关联起来,Brendan Eich又不希望把js设计的像JAVA和C++一样复杂,最终决定用prototype__proto__来模仿类,实现“继承”的功能。JS历史

? 受到C++和Java语言的启发,它们都使用new命令,生成实例。

// C++
Foo* foo = new Foo();
// java
Foo foo = new Foo();

因此,作者便把new命令引入了Javascript,用来从原型对象生成一个实例对象
但Javascript没有”类”,怎么来表示原型对象(class中的父类)呢? → 构造函数?

// 函数名首字母大写,为了区分普通函数
function Person(name) {
   this.name = name;
}

var person1 = new Person('jennifer');
alert(person1.name); // jennifer

上面的代码中,new关键字做了什么呢?

  1. 创建一个新对象
  2. 将构造函数的作用域赋值给新对象,this指向新对象
  3. 执行构造函数中的方法,为新对象添加属性
  4. 返回新对象

new的缺点

? 如果实例需要公用一个属性或方法,无法共享属性和方法

function Person(name) {
   this.name = name;
   this.species = '人类';
}

var person1 = new Person('jack');
var person2 = new Person('rose');
person1.species = '白种人';
console.log(person2.species); // 人类

解决: 构造函数的prototype属性

prototype属性

作用:存放实例对象需要共享的属性和方法的对象,实例对象一旦创建,将自动引用prototype对象的属性和方法

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

Person.prototype = {
    species = '人类'
}

var person1 = new Person('jack');
var person2 = new Person('rose');
console.log(person1.species, person2.species); // 人类, 人类

prototype可以说是person1person2的父类,为它们提供了共享属性species。所有对象,都可以作为另一个对象的prototype来用。那一个对象如何为另一个对象提供属性访问呢?答案是proto

__proto__

js中任意对象都有一个内置属性 [[Prototype]],在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过__proto__来访问原型,甚至能通过obj.__proto__ = anotherObj直接设置原型。在开发者工具中看到访问一个对象能看到里面存在__proto__属性,但这只是方便开发者查看原型渲染出的虚拟节点,实际上并不在该对象里。访问对象的obj.__proto__属性,默认走的是 Object.prototype对象上__proto__属性的 get/set 方法。

所以根据上述例子,person1.__proto__ = Person.prototype

只有函数有prototype属性,__proto__存在于对象上,Function也是对象,所以也有__proto__,他指向谁呢?

原型链(js的继承本质)

通过prototype__proto__在两个对象之间创建一个关联,使得一个对象就可以通过委托访问另一个对象的属性和函数。前面我们说过prototype也是一个对象,他也有自己的proto,这就形成了一条链条,直到protonull,整条链条终止。

看一张图
2a55ed6a-d7c1-4643-bc8f-73b78773dd8b

如何实现new

function Dog(name){
    this.name = name || 'sam';
}

function _new(fn) {
    const obj = Object.create(fn.prototype);
    const result = fn.apply(obj, arguments);
    return typeof result === 'object' ? result : obj;
}

const dog1 = _new(Dog)('lucky');
console.log(dog1.name); // lucky