JavaScript 原型 -2 - JS中原型prototype,__proto__ 原型链,继承
构造函数和对象
public class Person{
String breed;
int age;
String color;
void barking(){
}
void hungry(){
}
void sleeping(){
}
}
但是在ES6之前都没有class,那么JS怎么定义类呢? 在JS中函数是一等公民,我们可以通过构造函数来创建对象.
所谓构造函数,就是提供了一个生成对象的模板并描述对象的基本结构的函数.一个构造函数,可以生成多个对象,每个对象都有相同的结构.
总的来说,构造函数就是对象的模板,对象就是构造函数的实例.
// Person类
function Person(name) {
// 实例属性
this.name = name
// 实例方法
this.sayName = function() {
return this.name
}
}
const p1 = new Person('leslie-1')
const p2 = new Person('leslie-2')
console.info(p1.constructor === Person) // true
我们通过new来构建实例化对象,类函数中的 this 总是指向实例话的对象.
每一个实例对象都有一个不可枚举的属性 constructor来指向构造函数,即 Person

打印一下 实例p1 和 p1的构造函数


实例对象中我们可以看到在类中定义的 name 属性 和 sayName方法都有了,但是 constructor 属性并没有,但是却能取到值。
构造函数缺点
所有的实例对象都会单独创建自己的属性和方法,不同实例对象之间无法共享通用的属性
p1.sayName() // leslie-1
p2.sayName() // leslie-2
p1.sayName == p2.sayName // false
但是有的属性或者方法是共有的,我们希望每个实例对象创建的时候就能有。比如每个人天生会哭 cry。
原型对象prototype
为了解决实例对象之间共享属性的问题,JS提供了prototype属性
prototype是从一个函数指向一个对象,,也就是说 函数才有 prototyoe属性
它的作用是让该构造函数创建的所有实例对象都能找到公用的属性和方法。
任何函数在创建实例对象的时候, 会去关联该函数的prototype对象.

需要注意的是, 我们可以修改原型对象的应用,但是仍需要把 constructor 属性指向回构造函数。
Person.prototype = {
constructor: Person,
cry: function(){
console.info('cry')
}
}
为什么 constructor 属性指向回构造函数。

原型链
有了原型对象,我们知道了, 实例对象的属性和方法,可能是定义在自身(构造函数),也有可能是定义在它的原型对象上。
通过上面的cry函数我们可以看出,实例对象能够直接获取原型对象上的属性和方法. 它是怎么获取的呢?
打印p1, 我们发现一个特别的属性 proto

因此,proto指向了实例对象的原型对象
当访问一个对象上没有的属性时,对象就会去proto上面找
如果还是找不到,就会继续找原型对象的proto,直到原型对象为了null
因此proto构成了一条原型链
(p1.proto === Person.prototype)
同时我们也解答了上面实例对象上没有 constructor属性的问题, constructor属性真正存于原型对象上,所以实例对象才能获取到。(虚线表示 该属性或者方法并不是真正存在的)(实例对象的构造函数在原型上[proto])

同时,原型对象也是对象,既然是对象,那么肯定有自己的原型对象,那么它的原型对象是谁呢? 我们知道, JS中所有的对象都是 Object的实例,并继承 Object.prototype的属性和方法; 字面量 var = {} 实际上也是 new Object 的语法糖,因此
var obj1 = {}
var obj2 = new Object()
obj1.__proto__ === Object.prototype // true
obj2.__proto__ === Object.prototype // true
Person.prototype.__proto__ === Object.prototype // true

constructor
constructor 用来指向构造函数,同时 constructor真正存在于原型对象上,因此
Person.prototype.constructor = Person // true
在学习类型判断的时候学过, constructor可以用来进行数据类型的判断
console.info((2).constructor === Number) // true
console.info((true).constructor === Boolean) // true
console.info(('str').constructor === String) // true
console.info(([]).constructor === Array) // true
console.info((function(){}).constructor === Function) // true
console.info(({}).constructor === Object) // true

// 这种方式看起来能判断所有类型,但是一旦我们更改了原型对象,这种方式就不可靠了
function Fn(){}
Fn.prototype = new Array()
var f = new Fn()
console.info(f.constructor === Fn) // false
console.info(f.constructor === Array) // true
在JS中, 函数本身也可以看成是对象, 对这种又是函数,又是对象,有一个特殊的称呼: 函数对象
我们调用函数的 fn.apply 和 fn.call 其实调用的是继承自原型对象上的
Function.prototype.call 和 Function.prototype.apply
因此函数都是 Function函数的实例对象, 既然是实例对象, 所以 函数 也拥有 proto 和 constructor属性。
//ƒ Function() { [native code] }
console.log(Person.constructor)
//true
Person.constructor === Function
//false
Person.hasOwnProperty('constructor')
//ƒ () { [native code] }
console.log(Person.__proto__)
//true
Person.__proto__.hasOwnProperty('constructor')
//true
Function.prototype.hasOwnProperty('constructor')
可以看出来, ** Person构造函数和JS普通的对象没有任何区别 **, 有自己的constructor属性, 指向 Function 函数, 说明 Person函数是 Function 函数的实例对象; 而且 constructor属性不在Person本身,而在其原型对象 Function.prototype上
console.info(Person.constructor === Function) // true, 使用原型链查找
console.info(Person.__proto__.constructor === Function) // true

鸡生蛋蛋生鸡
到这里, 我们发现最终原型图指向了4个基本的东西: Object, Object.prototype,Function, Function.prototype,拆开来看:

我们都知道 Object函数 和 Person函数, 都是函数对象, 因此都是 Function函数的实例对象
Object.constructor === Function // true
Object instanceof Function // true
Object instanceof Object // true
Object.hasOwnProperty('constructor') // false
Object.__proto__.hasOwnProperty('constructor') // true
Object.__proto__ === Function.prototype // true
因此我们完善 Object 和 Function的关系

Object 是构造函数, 我们也知道 Function 也能通过 new Function来构造匿名函数,同时自己又是自己的 constructor
Function.constructor === Function // true
Function instanceof Object // true
Function instanceof Function // true
Function.hasOwnProperty('constructor') // false
Function.__proto__ === Function.prototype
同时 Function.prototype 和 Person.prototype一样是个对象,因此它的原型对象肯定就是 Object.prototype
Function.prototype.__proto__ === Object.prototype // true

现在最完整也是最复杂的原型链出现了:
Object构造函数,继承了Function.prototypeFunction函数也是对象, 继承了Object.prototype- 那么到底是先有了
Object还是先有的Function
我们发现导致鸡和蛋的问题根本原因在于 Function.__proto__ 指向了 Function.prototype
让 Function 继承了 Object.prototype上的方法。
因此我们需要对 Function.prototype来进一步的了解.
typeof Function.prototype // function
Function.prototype() // undefined
Function.prototype instanceof Object // true
Function.prototype instanceof Function // false
Function.prototype.prototype // undefined
我们发现 Function.prototype 是个特殊的函数对象, 没有 prototype属性.也就是说:
Function.prototype像普通函数一样可以调用,但是总返回undefinedFunction.prototype继承于Object.prototype,并没有prototype属性- 因此
Function.prototype是个标准的内置对象.它继承于Object.prototype - 而
Object.prototype===null,说明 原型链到Object.prototype就中止了

浙公网安备 33010602011771号