对象
- JavaScript 分为函数对象和普通对象,每个对象都有__proto__属性(每个对象都有的隐式原型属性,指向了创建该对象的构造函数的原型。如示1 其实这个属性指向了
[[prototype]]
,但是 [[prototype]]
是内部属性,我们并不能访问到,所以使用__proto__
来访问), 但是只有函数对象才有prototype
属性。
__proto__
读音: dunder proto
示1
每个对象都有的隐式原型属性,指向了创建该对象的构造函数的原型。__proto__
可以通过Object.getPrototypeOf()
获取
const obj = { a: 123 }
obj.__proto__ === Object.prototype // true
function Person(name) {
this.name = name
}
const Tom = new Person('tom')
Tom.__proto__ === Person.prototype // true
Object.getPrototypeOf(Tom) === Person.prototype // true
const arr = [1,2,3]
arr.__proto__ === Array.prototype
// Array是函数对象,是Function的实例对象,Array是通过new Function创建出来的。因为Array是Function的实例,所以Array.__proto__ === Function.prototype
Array.__proto__ === Function.prototype
Function.prototype.__proto__ === Object.prototype
普通对象
- 最普通的对象:有
__proto__
属性(指向其原型链),没有prototype
属性。 - 原型对象(Person.prototype 原型对象还有constructor属性(指向构造函数对象))
函数对象
- 凡是通过
new Function()
创建的都是函数对象 - 拥有
__proto__
、prototype
属性(指向原型对象) - 包括Function、Object、Array、Date、String、自定义函数
- 特例:
Function.prototype
(是原型对象,却是函数对象) - 箭头函数没有
prototype
;- 箭头函数是匿名函数,是不能作为构造函数的,不能使用
new
; - 箭头函数不绑定
arguments
,取而代之用rest参数(...rest)
解决; - 箭头函数不能当做
Generator
函数,不能使用yield
关键字; - 箭头函数会捕获其所在上下文的
this
值,作为自己的 this 值,任何方法都改变不了其指向,如call()
,bind()
,apply()
,而普通函数的this指向调用它的那个对象。
- 箭头函数是匿名函数,是不能作为构造函数的,不能使用
//函数对象
function F1(){};
const F2 = function(){};
const F3 = function(n1, n2) { return n1 + n2};
console.log(typeof F1); //function
console.log(typeof F2); //function
console.log(typeof F3); //function
console.log(typeof Object); //function
console.log(typeof Array); //function
console.log(typeof String); //function
console.log(typeof Date); //function
console.log(typeof Function); //function
原型对象(函数的prototype
属性)
每创建一个函数都会有一个
prototype
属性,这个属性是一个指针,指向一个对象(通过该构造函数创建实例对象的原型对象)。原型对象是包含特定类型的所有实例共享的属性和方法。 原型对象的好处是,可以让所有实例对象共享它所包含的属性和方法。
function Person(){};
console.log(typeof Person.prototype) //Object
console.log(typeof Object.prototype) // Object
console.log(typeof Function.prototype) // 特殊 Function
console.log(typeof Function.prototype.prototype) //undefined 函数对象却没有prototype属性
原型对象、构造函数、实例对象之间的关系
function Dog(){};
Dog.prototype.name = "小黄";
Dog.prototype.age = 13;
Dog.prototype.getAge = function(){
return this.age;
}
var dog1 = new Dog();
var dog2 = new Dog();
dog2.name = "小黑";
console.log(dog1.name); // 小黄 来自原型
console.log(dog2.name); // 小黑 来自实例
示图
dog1.__proto__ === Dog.prototype // true
Dog.prototype.__proto__ === Object.prototype //true
dog1.__proto__.__proto__ === Object.prototype // true
Dog.prototype.constructor === Dog // true
Dog.prototype.isPrototypeOf(dog1) // true
//获取对象的原型
dog1.__proto__ //不推荐
Object.getPrototypeOf(dog1) === Dog.prototype //推荐
原型链
原型链基本思路:
利用原型让一个引用类型继承另一个引用类型的属性和方法。
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数想指针(constructor
),而实例对象都包含一个指向原型对象的内部指针(__proto__
)。 如果让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针(__proto__
),另一个原型也包含着一个指向另一个构造函数的指针(constructor)。假如另一个原型又是另一个类型的实例……这就构成了实例与原型的链条。
原型链基本思路(图解): 举例说明:
function Animal(){
this.type = "animal";
}
Animal.prototype.getType = function(){
return this.type;
}
function Dog(){
this.name = "dog";
}
Dog.prototype = new Animal();
Dog.prototype.getName = function(){
return this.name;
}
var xiaohuang = new Dog();
//原型链关系
xiaohuang.__proto__ === Dog.prototype
Dog.prototype.__proto__ === Animal.prototype
Animal.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
instanceof
检测的是原型
[] instanceof Array; // true
const obj = {}
obj instanceof Object;// true
new Date() instanceof Date;// true
function Person(){};
new Person() instanceof Person; // true
[] instanceof Object; // true
new Date() instanceof Object;// true
new Person() instanceof Object;// true
instanceof 能够判断出 []._proto_
指向 Array.prototype
,而 Array.prototype._proto_
又指向了Object.prototype
,最终 Object.prototype._proto_
指向了null
,标志着原型链的结束。因此,[]、Array、Object 就在内部形成了一条原型链:
Function 和 Object
- function Object()也是个函数,所以我们可以认为Object()是通过 new Function() 出来的,所以Object()此时是实例对象,实例对象上面一定有__proto__属性,所以
Object.__proto__
===Function.prototype
- function Function()同样也是函数,我们也可以认为他是通过 new Function() 出来的,所以Function()此时是实例对象,实例对象上面一定有__proto__属性,所以
Function.__proto__
===Function.prototype
- 我们说过任何东西都是new Object()所出来的,所以Function() 也是new Object() 出来的,此时Function()是实例对象,实例对象上面一定有__proto__属性,但是Function的__proto__属性的连线已经连到了Function的显式原型对象,所以它通过上图的红线进行连接,
Function.__proto__.__proto__
===Object.prototype
是(true),即Function.prototype.__proto__ === Object.prototype
类的 prototype 属性和__proto__属性
大多数浏览器的 ES5 实现之中,每一个对象都有__proto__
属性,指向对应的构造函数的prototype
属性。Class
作为构造函数的语法糖,同时有prototype
属性和__proto__
属性,因此同时存在两条继承链。
(1)子类的
__proto__
属性,表示构造函数的继承,总是指向父类。(2)子类
prototype
属性的__proto__
属性,表示方法的继承,总是指向父类的prototype
属性。
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
上面代码中,子类B的__proto__
属性指向父类A,子类B的prototype
属性的__proto__
属性指向父类A的prototype
属性。 这两条继承链,可以这样理解:作为一个对象,子类(B)的原型(__proto__
属性)是父类(A);作为一个构造函数,子类(B)的原型对象(prototype
属性)是父类的原型对象(prototype
属性)的实例。 这样的结果是因为,类的继承是按照下面的模式实现的。
class A {
}
class B {
}
// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);
const b = new B();
总结
- 1、构造函数的原型链
function Person(name) {
this.name = name
}
const Tom = new Person('tom')
// 1、实例Tom 的_proto_ 指向构造函数person的原型对象prototype
console.log(Tom.__proto__ === Person.prototype) // true
// 2、构造函数Person的原型对象prototype的__proto__指向Object的原型对象prototype
console.log(Person.prototype.__proto__ === Object.prototype) // true
// 3、构造函数Object的原型对象prototype的__proto__指向null
console.log(Object.prototype.__proto__ === null) // true
// 其中 Person.prototype的constructor属性在没修改的情况下指向自身
console.log(Person.prototype.constructor === Person)
// 其中 Person在作为函数对象拥有prototype的同时,也是普通对象拥有__proto__,且指向Function.prototype
console.log(Person.__proto__ === Function.prototype) // true
参考