JS中关于原型对象与原型链的理解!
1、首先我们先来看一张图
prototype
每个函数都有一个 prototype 属性
每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。
demo:
function Person() { } // 虽然写在注释里,但是你要注意: // prototype是函数才会有的属性 Person.prototype.name = 'Kevin'; var person1 = new Person(); var person2 = new Person(); console.log(person1.name) // Kevin console.log(person2.name) // Kevin
构造函数Person可以通过prototype属性访问到它的原型对象
function Person() { } // 虽然写在注释里,但是你要注意: // prototype是函数才会有的属性 console.log(Person.prototype)
通过构造函数Preson实例化出来的person可以通过__proto__属性访问到Man的原型对象
function Person() { } var person = new Person(); console.log(person.__proto__ === Person.prototype); // true
Person的原型对象可以通过constructor(构造器)属性访问其关联的构造函数
function Person() { } console.log(Person === Person.prototype.constructor); // true
so 我们可以通过三种方式来访问原型对象
1.构造函数.prototype
2.实例对象.__proto__
3.object.getPrototypeOf(实例对象)
原型链的访问规则
对象在访问属性或方法时,先检查自己的实例,如果存在就直接使用。如果不存在那么就去原型对象上去找,存在就直接使用,如果没有就顺着原型链一直往上查找,找到即使用,找不到就重复该过程直到原型链的顶端,如果还没有找到相应的属性或方法,就返回undefined,报错
function Person() { } Person.prototype.name = 'Kevin'; var person = new Person(); person.name = 'Daisy'; console.log(person.name) // Daisy delete person.name; console.log(person.name) // Kevin
在这个例子中,我们给实例对象 person 添加了 name 属性,当我们打印 person.name 的时候,结果自然为 Daisy。
但是当我们删除了 person 的 name 属性时,读取 person.name,从 person 对象中找不到 name 属性就会从 person 的原型也就是 person.proto ,也就是 Person.prototype中查找,幸运的是我们找到了 name 属性,结果为 Kevin。
原型与原型(看头部大图理解)
var obj = new Object(); obj.name = 'Kevin' console.log(obj.name) // Kevin
console.log(Object.prototype.__proto__ === null) // true
三种检验方法
Object.getPrototypeOf方法用于获取指定实例对象的原型对象。
//01 声明构造函数F function F() {} //02 使用构造函数F获取实例对象f var f = new F(); //03 测试getPrototypeOf方法的使用 console.log(Object.getPrototypeOf(f)); //打印的结果为一个对象,该对象是F相关联的原型对象 console.log(Object.getPrototypeOf(f) === F.prototype); //true console.log(Object.getPrototypeOf(f) === f.__proto__); //true
isPrototypeOf方法用于检查某对象是否在指定对象的原型链中。
function Demo(){} var demo = new Demo(); console.log(Demo.prototype.isPrototypeOf(demo)); // true console.log(Object.prototype.isPrototypeOf(demo)); //true
console.log(Object.prototype === person.__proto__.__proto__) //true
instanceof运算符的作用跟isPrototypeOf方法类似,左操作数是待检测的实例对象,右操作数是用于检测的构造函数。如果右操作数指定构造函数的原型对象在左操作数实例对象的原型链上面,则返回结果true,否则返回结果false。
// 1 声明构造函数Demo function Demo(){}; // 2 获取实例化对象demo var demo = new Demo(); // 3 使用isPrototypeOf console.log(demo instanceof Demo); //true console.log(demo instanceof Object); //true // 4 Object构造函数的原型对象在Function这个实例对象的原型链中 console.log(Function instanceof Object); //true
console.log(Object.prototype === Function.__proto__.__proto__); //true
// 5 Function构造函数的原型对象在Object这个实例对象的原型链中 console.log(Object instanceof Function); //true
console.log(Function.prototype === Object.__proto__); //true
注意:不要错误的认为instanceof检查的是该实例对象是否从当前构造函数实例化创建的,其实它检查的是实例对象是否从当前指定构造函数的原型对象继承属性。
//01 声明构造函数Person function Person() {} //02 获取实例化对象p var p1 = new Person(); //03 测试isPrototypeOf的使用 console.log(p1 instanceof Person); //true //04 替换Person默认的原型对象 Person.prototype = { constructor:Person, showInfo:function () { console.log("xxx"); } }; //05 重置了构造函数原型对象之后,因为Person console.log(p1 instanceof Person); //false //06 在Person构造函数重置了原型对象后重新创建实例化对象 var p2 = new Person(); console.log(p2 instanceof Person); //true //==> 建议开发中,总是先设置构造函数的原型对象,之后在创建实例化对象
原型链相关的继承
原型式继承基本写法
//01 提供超类型|父类型构造函数 function SuperClass() {} //02 设置父类型的原型属性和原型方法 SuperClass.prototype.info = 'SuperClass的信息'; SuperClass.prototype.showInfo = function () { console.log(this.info); }; //03 提供子类型 function SubClass() {} //04 设置继承(原型对象继承) SubClass.prototype = SuperClass.prototype; SubClass.prototype.constructor = SubClass; var sub = new SubClass(); console.log(sub.info); //SuperClass的信息 sub.showInfo(); //SuperClass的信息
原型链继承
实现思想
核心:把父类的实例对象设置为子类的原型对象 SubClass.prototype = new SuperClass();
问题:无法为父构造函数(SuperClass)传递参数
//01 提供超类型|父类型 function SuperClass() { this.name = 'SuperClass的名称'; this.showName = function () { console.log(this.name); } } //02 设置父类型的原型属性和原型方法 SuperClass.prototype.info = 'SuperClass的信息'; SuperClass.prototype.showInfo = function () { console.log(this.info); }; //03 提供子类型 function SubClass() {} //04 设置继承(原型对象继承) var sup = new SuperClass(); SubClass.prototype = sup; SubClass.prototype.constructor = SubClass; var sub = new SubClass(); console.log(sub.name); //SuperClass的名称 console.log(sub.info); //SuperClass的信息 sub.showInfo(); //SuperClass的信息 sub.showName(); //SuperClass的名称
组合继承
实现思想
① 使用原型链实现对原型属性和方法的继承
② 通过伪造(冒充)构造函数来实现对实例成员的继承,并且解决了父构造函数传参问题
//01 提供超类型|父类型 function SuperClass(name) { this.name = name; this.showName = function () { console.log(this.name); } } //02 设置父类型的原型属性和原型方法 SuperClass.prototype.info = 'SuperClass的信息'; SuperClass.prototype.showInfo = function () { console.log(this.info); }; //03 提供子类型 function SubClass(name) { SuperClass.call(this,name); } //(1)获取父构造函数的实例成员 Person.call(this,name); //(2)获取父构造函数的原型成员 SubClass.prototype = SuperClass.prototype; SubClass.prototype = SuperClass.prototype; SubClass.prototype.constructor = SubClass; var sub_one = new SubClass("zhangsan"); var sub_two = new SubClass("lisi"); console.log(sub_one); console.log(sub_two);