javascript是唯一一个被广泛使用的基于原型继承的语言。
在JavaScript中,万物皆对象。
构造函数
function Person() { }
var person = new Person();
person.name = "张三"
我们创建一个Person
构造函数,使用new
来创建一个实例对象person
。
prototype
为了解决构造函数的对象实例之间无法共享属性的缺点,JavaScript提供了prototype
属性。prototype
就是“一个给类的对象添加方法的方法”,使用prototype属性,可以给类动态地添加方法,以便在JavaScript中实现”继承”的效果。
JavaScript中每个对象都有prototype
属性,它本身也是对象,JavaScript中对象的prototype
属性的解释是:返回对象类型原型的引用。
function Person() {}
Person.prototype.name="张三";
var person1 = new Person();
var person2 = new Person();
console.log(person1.name); // 张三
console.log(person2.name); // 张三
上例中,将name
属性放在prototype
中,两个实例person1
和 person2
都共享同一个属性。
对于构造函数来说,prototype
是作为构造函数的属性;对于对象实例来说,prototype
是对象实例的原型对象,所以prototype
既是属性又是对象。
什么是原型?
每一个JavaScript对象(除null、undefined外)在创建的时候就会与之关联另一个对象,这个对象就是我们说的原型,每一个对象都会从原型"继承"属性。
__proto
每个对象(除null、undefined)都会在其内部初始化一个属性,就是__proto__
,这个属性会指向对象的原型。
__proto__
是不对外公开的,是个私有属性,但是Firefox和Chrome的引擎将其暴露出来成为一个公共属性,我们可以对外访问和设置。所以我们可以使用Object.getPrototypeOf(obj)
代替 __proto__
。
function Person() {}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
constructor
constructor
属性, 构造函数,始终指向创建当前对象的构造函数。
每个函数都有一个prototype
属性,而constructor
是prototype
的属性,即对象实例的属性, 而prototype
是函数的属性。
function Person() {}
var person = new Person();
console.log(Person === Person.prototype.constructor); // true
console.log(person.constructor === Person); // true
说明: 当获取person.constructor
时, 其实person
中并没有constructor
属性, 当不能读取到constructor
属性时,会从person
的原型也就是Person.prototype
中读取,正好原型中有该属性。所以person.constructor === Person.prototype.constructor
。
__proto__
、prototype
、cosntructor
之间的联系:
function Person() {}
var person1 = new Person();
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person === Person.prototype.constructor); // true
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
实例与原型
当读取实例的属性时, 如果找不到,就会查找与对象关联的额原型中的属性,如果还查不到,就去原型的原型,一直找到最顶层位置。
function Person() {}
Person.prototype.name = '张三';
var person = new Person();
person.name = '李四';
console.log(person.name); // 李四
delete person.name;
console.log(person.name); // 张三
这个例子中,我们设置了person
中的name
属性,所以我们读取到“李四”, 当我们删除了person
的name
属性时,读取person.name
, 从person
中找不到就会从person
的原型也就是person.__proto__ === Person.prototype
中查找,哎,发现找到了name
, 则输出”张三”。 如果没有找到呢?原型的原型又是什么?
使用最原始的方式创建:
var obj = new Object();
obj.name = '张三';
console.log(obj.name);
所以原型对象是通过Object
构造函数生成的, 所以….
原型链
原型链并不是无限长,也有尽头—–null
。
图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的路线。
end
继承意味着复制操作,然而JavaScript默认并不会复制对象的属性,相反,JavaScript只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。