目录
3. __proto__表示隐式原型,prototype表示显式原型
constructor属性(__proto__原型对象中包含constructor属性,实例当中也同样会继承这个属性): 实例对象person -》 __proto__ -》constructor(Person)
prototype属性(constructor.prototype原型对象):constructor -》 prototype
一、原型
1. 原型概念:
每个函数和类都有一个显式原型prototype,里面存放一些公用的属性和方法,这些方法和属性,其每一个实例都能访问到。
而且每一个实例的对象都有一个隐式的原型__proto__,对象的隐式原型都指向构造这个对象的函数或类的显式原型。
使用原型对象的好处:是可以让所有对象实例共享它所包含的属性和方法
2. 原型示例:
定义Student类 ,实例化该类并传入'聪聪',99,得到一个实例对象stu。
该实例对象中有一个__proto__属性,即为该对象的隐式原型。
stu.play(): 在对象stu寻找play方法,=> 若在该对象中找不到,=> 则去其隐式原型__proto__中去寻找
3. __proto__表示隐式原型,prototype表示显式原型
实例对象的隐式原型 指向 构建该实例对象的类的显式原型,即
4. 构造函数,实例对象和原型对象是怎么挂载的?
构造函数和实例对象不直接相连,实例对象的隐式原型__proto__指向构造函数的显示原型prototype。即都指向实例对象。
5. 获取原型的3种方法?
function R() {
}
var one = new R();
console.log(Object.getPrototypeOf(one)); //方法1:官方推荐,规范写法
console.log(one.__proto__); //方法2:不推荐
console.log(one.constructor.prototype) //方法3:不推荐
二、原型链
1. 原型链示例
Person类 -》 继承Person的Teacher类 -》 创建Teacher类的实例对象teacher
(1)teacher.teach(); 调用teach方法
过程:1) teacher先在自身查找,如果没有teach方法,就去隐式原型__proto__查找 (teacher的隐式原型__proto__ 指向 Teacher类的显式原型prototype)
2)Teacher类的显式原型prototype中有teach方法,直接调用
(2)teacher.drink(); 调用drink方法
过程:1) teacher先在自身查找,如果没有drink方法,再去其隐式原型__proto__查找 (teacher的隐式原型__proto__ 指向 Teacher类的显式原型prototype)
2)Teacher类的显式原型prototype中没有drink方法,再去Teacher类显式原型的隐式原型__proto__中查找(Teacher类显式原型的隐式原型 指向Person类的显式原型 )
3) Person类的显式原型prototype中有drink方法,调用
2. 完整的原型链结构
到Object对象终止
三、原型和原型链常考面试题
(1)构造函数的原型
因为是一个函数,所以原型为Function。
构造函数实例对象的隐式原型 指向 构造函数的显示原型
let f = new Function(); //构造函数Function 的实例对象为f;
console.log(Function.prototype) //指向“内置函数” ƒ () { [native code] }
console.log(f.__proto__) //指向“内置函数” ƒ () { [native code] }
console.log(f.__proto__===Function.prototype) //true
console.log(Object.prototype.toString.call(f.__proto__)) // [object Function]
(2)Object的原型
一切对象都有一个根源 Object.prototype,根源之上再没有其它根源:
let obj = new Object(); //Object的实例对象obj。等价于 let obj = {}
console.log(obj.__proto__ === Object.prototype) //true。
console.log(Object.prototype) // Object。 指向"根源对象"(原型链最顶层)
console.log(obj.__proto__ ) // Object。指向"根源对象"(原型链最顶层)
console.log(Object.prototype.__proto__) //null 。
Object.getPrototypeOf(obj): 返回指定对象obj的原型
(3)原型和原型链的面试题1
//有关原型与原型链的面试题目
{
function Fn(){
this.x = 100;
this.y = 200;
this.getX = function () {
console.log(this.x);
}
}
Fn.prototype.getX = function () {
console.log(this.x);
};
Fn.prototype.getY = function () {
console.log(this.y);
};
var f1 = new Fn;
var f2 = new Fn;
console.log(f1.getX === f2.getX); //false
console.log(f1.getY === f2.getY); //true
console.log(f1.__proto__.getY === Fn.prototype.getY); //true
console.log(f1.__proto__.getX === f2.getX); //false
console.log(f1.__proto__.getX === Fn.prototype.getX); //true
console.log(f1.constructor); //Fn
console.log(Fn.prototype.__proto__.constructor); //Object !!!
f1.getX(); //100 this=>f1
f1.__proto__.getX(); //undefined this=>f1.__proto__(Fn.prototype)
f2.getY(); //200 this=>f2
Fn.prototype.getY(); //undefined this=>f2.__proto__(Fn.prototype)
}
// false,true,true,false,true,Fn,Object,100,undefined,200,undefined
以 let Peppa = new Pig() 为例 :
(4)原型和原型链的面试题2
// 原型、原型链
{
function fun(){
this.a = 0;
this.b = function(){
console.log(this.a);
}
}
fun.prototype = {
b: function(){
this.a = 20;
console.log(this.a);
},
c: function(){
this.a = 30;
console.log(this.a);
}
}
var my_fun = new fun();
my_fun.b(); // 打印0。 私有方法 this=>my_fun 打印0
console.log(my_fun.a); // 打印0。
my_fun.c(); //打印30。公有方法 this=>my_fun this.a = 30(将私有属性a修改为30)
console.log(my_fun.a); // 打印30.
var my_fun2 = new fun();
console.log(my_fun2.a); // 打印0。
my_fun2.__proto__.c(); //打印30. this=>my_fun2.__proto__ 当前实例通过原型链在类的共有属性上增加了一个a:30
console.log(my_fun2.a); //打印0
console.log(my_fun2.__proto__.a); //打印30
}
// 0,0,30,30,0,30,0,30
(5)原型和原型链的面试题3
function Foo() {
Foo.a = function() {
console.log(1)
}
this.a = function() {
console.log(2)
}
}
Foo.prototype.a = function() {
console.log(3)
}
Foo.a = function() {
console.log(4)
}
Foo.a(); //4. 构造函数Foo还未调用
let obj = new Foo();
obj.a(); //2. 构造函数Foo中,this指向obj,指向this.a
Foo.a(); //1. 构造函数Foo已调用,Foo.a 覆盖外层的Foo.a
// 结果:4 2 1
解析:
Foo.a() 的输出值为 4。此时并没有调用 Foo 构造函数。
obj.a() 的输出值为 2。虽然原型上也有 a 方法。但是实例对象 obj 上也有 a 方法,所以会先在自身找,找不到时才去原型链上找。
Foo.a() 的输出值为1。调用了构造函数 Foo 之后,Foo 内部的 this.a 方法覆盖了外层的 Foo.a
(6)原型和原型链的面试题4
写出以下实例对象的所有原型链。
function Person(name){
this.name = name;
};
var jack = new Person("jack");
解析:
实例对象的原型链:
jack.__proto__ === Person.prototype;
Person.prototype.__proto__ === Object.prototype;
jack.__proto__.__proto__ === Object.prototype;
构造函数的原型链:
Person.__proto__ === Function.prototype;
Function.prototype === Function.__proto__;
Function.prototype.__proto__ === Object.prototype;
Function.__proto__.__proto__ === Object.prototype;
Person.__proto__.__proto__ === Object.prototype;
四、constructor属性
1. 每个实例对象都有隐式原型__proto__,该原型的constructor属性,指向创建这个对象的构造函数
例如:对象person的constructor属性,指向构造函数Person
2.每个函数都有显式prototype属性,该原型的constructor属性会指向这个函数(其 实例对象的__proto__ 指向 其prototype,故指向的是同一个constructor)。
如果我们重写(重新定义)这个Person.prototype属性,那么constructor属性的指向就会发生改变了。