主要是利用了__proto__这个隐式原型将所有的继承串在了一起,实现了原型链
下面是一个new itemTest()的实例,它的__proto__就指向了继承的itemTest的prototype,然后再向上__proto__就是test的prototype,由此构成了原型链
- 每个对象都有一个原型,每个对象的原型也可以拥有一个原型,从而形成原型链
- 几乎所有 JavaScript 中的对象都是位于原型链顶端的Object的实例
- 查找特定属性(方法)将会被委托在整个原型链上,先查找实例本身,没有的话再到其原型上,往上一层层,直到没有更多的原型才停止查找,返回null
- ECMAScript将原型链作为实现继承的主要方法,其基本思想就是利用原型让一个引用继承另一个引用类型的属性和方法
function test(name, age, sex) {
this.name = name;
}
test.prototype={
sayName: function(){
return this.name;
}
};
var t = new test("zjf");
console.log(t.sayName()); //"zjf"
var t1=new test("lll");
console.log(t1.sayName()); //"lll"
- 以下代码,前者会覆盖test.prototype中的所有属性和方法,后者是添加或者修改指定的属性和方法,建议后者书写
- 改变原型需注意和创建实例的顺序,因为对象与函数原型之间的引用关系是在创建时建立的,所以如果在创建实例后改变原型,实例依旧会继承旧的原型,而在创建实例前改变,则会继承新的原型
test.prototype = {
sayName: function(){
return this.name;
}
};
test.prototype.sayName = function(){
return this.name;
};
- 继承原型函数的实例无法改变原型的属性和值(实例自己可以设置自己的属性和值)
var t=new test();
t.name = 'mmm';
t.sayName = function() {
return "111";
}
console.log(t.sayName()); // "111"
原因:一个是定义在实例对象上的,一个是在原型链上的,不冲突,但是不建议这么做
- 每一个原型对象都有一个constructor指向它的构造函数,每一个实例也有
- 每一个object对象都有__proto__属性,且包括很多的方法,但只有函数对象有prototype属性
- 原型链的继承:itemTest继承了test的原型,通过itemTest创建实例item
function test(name) {
this.name = name;
}
test.prototype.sayName = function(){
return this.name;
};
function itemTest(name,age) {
// 继承父类的构造器
test.call(this, name)
this.age = age;
};
itemTest.prototype = new test();
// 给原型添加方法一定要放在替换方法前面
itemTest.prototype.sayAge = function(){
return this.name;
};
itemTest.prototype.sayName = function(){
return this.name;
};
var item = new itemTest("zjf");
console.log(item.sayName()); //"zjf"
console.log(item),可以看出实例本身没有sayName()方法,那么它就会沿着原型链一层层往上找
但是 itemTest.prototype.constructor指向了test,可以通过以下代码改变其指向
itemTest.prototype.constructor = itemTest;
或
Object.defineProperty(itemTest.prototype, "constructor", {
enumerable: false,
value: itemTest,
writeable: true
});
console.log(itemTest.prototype.constructor); //指向itemTest
原型链有一个问题:没有继承类型属性,可通过借用构造函数来解决这个问题,与原型链结合,调用上级的构造函数
function Super(name) {
this.name = name;
}
function Sub(name, age) {
//调用构造函数
Super.call(this, name);
this.age = age;
}
- 确定原型和实例的关系:instanceof操作符和isPrototypeof方法
console.log(item instanceof itemTest); //true
console.log(item instanceof test); //true
console.log(item instanceof Object); //true
使用Object.create()创建一个新对象,新对象的原型就是该方法的第一个参数
let a= {name: 'zjf'};
let b = Object.create(a);
console.log(Object.getPrototypeOf(b)); //{name: "zjf"}
Object.getPrototypeOf() 方法传入一个对象作为参数,查询该参数的原型,或者原型链上的属性或方法
let a= {name: 'zjf'};
let b = Object.create(a);
console.log(Object.getPrototypeOf(b)); //{name: "zjf"}
console.log(Object.getPrototypeOf(b).name); //"zjf"
Object.setPrototypeOf() 方法设置两个对象作为参数,第二个参数被设置为第一个参数的原型,但是性能上并不是很好,所以不建议采取
let a= {name: 'zjf'};
let b = {};
Object.setPrototypeOf(b,a);
console.log(Object.getPrototypeOf(b)); //{name: "zjf"}