原型
构造函数
1、let a = {} 等同于 let a = new Object() (a的构造函数是 Object 函数)
2、let a = [] 等同于 let a = new Array() (a的构造函数是 Array 函数)
3、function Foo {} 等同于 let Foo = new Function() (Foo 的构造函数是 Function 函数)
prototype
Javascript规定,每一个函数都有prototype的属性,叫做“原型”,prototype的所有属性和方法,都会被构造函数的实例继承,在开发的时候就可以把一些公用的属性和方法直接定义在prototype上。
function Foo() { };
let f = new Foo()
console.log(Foo.prototype);// Object{}
console.log(f.prototype);//undefined
proto
每一个对象都有*proto*的属性,叫做“隐式原型”,只有对象才有的属性,prototype是只有函数才有的属性。每一个对象的proto,指向创建该对象的函数的prototype。
//f是通过Foo构造出来的实例对象,那么
function Foo() { };
let f = new Foo()
console.log(f.__proto__ === Foo.prototype);//true
可能会有疑问,Foo是函数为什么返回的f却是对象。
console.log(Foo instanceof Object);//true
console.log(Foo instanceof Function);//true
console.log(Function instanceof Object);//true
console.log(Object instanceof Function);//true
函数属于对象,同时函数也可以创建对象。
所有的函数本质上都是有Function创建出来的,在开头构造函数提到。
let foo = new Function()
console.log(foo.__proto__ === Function.prototype);//true
//同理所有的对象本质上都是有Object创建出来的
let obj = new Object()
console.log(obj.__proto__ === Object.prototype);//true
constructor
每一个原型都有一个constructor属性constructor保存了指向function的一个引用。并且指向关联的构造函数,实例原型指向构造函数。
function foo() { }
let f = new foo()
/*
在前面构造函数提到过 foo其实是由 foo = new Function() 构造出来的
所以foo的constructor属性指向顶层函数Function,而Function的constructor指向自己
*/
console.log(foo.constructor);//ƒ Function() { [native code] }
//同理f是foo构造出来的所以f.constructor指向foo
console.log(f.constructor);//foo {}
//又例如
let obj = {}//等同于 obj = new Object()
console.log(obj.constructor === Object);//true
let arr = []//等同于 arr = new Array()
console.log(arr.constructor === Array);//true
原型链
function Foo() { }
let f = new Foo()
Foo.prototype.names = '张三'
console.log(f.names);//张三
Object.prototype.names = '张三'
function Foo() { }
let f = new Foo()
console.log(f.names);//张三
我们看到实例对象f自身并没有name属性,但是却能访问到。
Object.prototype.names = '张三'
Function.prototype.age = '20'
function Foo() { }
let f = new Foo();
console.log(f.names);//张三
console.log(f.age);// undefined
console.log(Foo.age);// 20
// 注意
console.log(Foo.prototype.__proto__ === Object.prototype);//true
f.age为什么会是undefined呢?Foo才是Function对象的一个实例,所以通过f.age应该可以访问到Function原型里的属性吧,但是new Foo()
返回的是一个对象,他是Object的一个实例,是没有继承Function的,所以无法访问到age。而所有的对象都可以看做Objec的实例(let obj = new Object()
),所以,
f.age => undefined;Foo.age => '20'
。
/*
f是实例对象,访问names属性发现自身没有,就会去访问__proto__对应的prototype(也就是Foo的原型),
还是发现没有,同理会查找Foo原型的__proto__,在向上找到Object的原型上有,结束。
*/
f.names => f.__proto__ => Foo.prototype => Foo.prototype.__proto__ => Object.prototype
作为一个对象,当你访问其中某个属性或方法时,如果这个对象本身并没有这个属性或方法,那么Javascript引擎将会通过这个对象的__proto__属性所指向的上一个对象,并在哪个对象中查找指定的方法或属性,如果还是不能找到,那么久会继续通过哪个对象__proto__属性指向的对象向上查找,直到这个链表结束。
prototype最主要的方法就是将属性暴露成公用的,也就是说通过__proto__原型链继承,看两个例子。
function Foo1(name, age) {
this.name = name;
this.sayHello = function () {
console.log(this.name);
}
}
let f1 = new Foo1('张三')
let f2 = new Foo1('李四')
console.log(f1.name);//张三
console.log(f2.name);//李四
console.log(f1.sayHello === f2.sayHello);//false
把方法和属性挂载到prototype上
function Foo2(name, age) {
this.name = name;
}
//把函数sayHello挂载到原型上
Foo2.prototype.sayHello = function () {
console.log(this.name);
}
let f3 = new Foo2('张三')
let f4 = new Foo2('李四')
console.log(f3.name);//张三
console.log(f4.name);//李四
console.log(f3.sayHello === f4.sayHello);//true
//把对象info挂载到原型上
Foo2.prototype.info = { name: '王五' }
console.log(f3.info === f4.info);//true
f3.info.name = '小明'
console.log(f4.info.name);//小明
可以看出结果当我们构造的实例对象去访问同一个属性或方法时,其实是同一个,通过原型链继承了这些方法和属性。