前言
从应用层面深入理解原型模式和js中的构造函数。
构造函数(constructor)
js中的任何对象都有自己的构造函数。js中使用字面量声明的普通对象({})或数组([])等子对象本质上都是使用相关的函数做构造调用声明的。
获取对象的构造函数:constructor是可读的,在对象上进行普通的属性访问(. / [])即可。
// 这里我们可以看到字面量语法声明的对象本质还是使用Object()构造的。
var obj = {}; console.log(obj.constructor); // ƒ Object() { [native code] }
原型
显式原型prototype属性
prototype是函数的属性。每个函数被创建之后,都会拥有一个prototype属性,这个属性指向函数的原型对象。
function fn (){} console.log(fn.prototype);
fn.prototype的原型在chrome下的输出。是一个对象,这个对象有一个显式的属性constructor(构造函数),它指向的是构造这个对象的函数。 还有一个隐式的属性,__proto__。
隐式原型__proto__属性
__proto__是对象的属性。每个对象被创建的时候都会拥有这个属性,这个属性指向的是,构造这个对象的函数的原型对象。
访问对象的__proto__属性:IE10以前无法识别__proto__属性,其他浏览器大部分都可以识别,可以输出但无法访问内部信心。ES5提供的Object.getPtotypeOf(obj) 可以获取指定对象的__proto__属性。
function fn (){} var obj = new fn(); console.log(fn.prototype === Object.getPrototypeOf(obj)); // true
搞清楚prototype/__proto__/constructor之间的区别和联系
1,所有对象都有自己的构造函数(constructor),和一个隐式的原型属性(__proto__),__proto__指向的是这个对象的构造函数的原型。
2,所有的函数都有一个prototype属性这个熟悉指向的是构造函数的原型对象。
var obj = {}; // 这里的true不理解的话回头再看一遍之前的小例子,敲一下。理解这个true的话 原型这个部分的应用就没问题了。
console.log(obj.constructor.prototype === Object.getPrototypeOf(obj)); // true
原型链
原型链就是一个对象作为另一个对象的原型而存,而这个作为别人原型的对象还有自己的原型。
根对象
js中的所有对象都拓展于Object.prototype。 它是js中的顶层对象。在它之上没有其他的原型了。
var obj= {}; var objConstructor = obj.constructor; // Object() 是使用js字面量创建的对象的构造函数。
console.log(objConstructor); // ƒ Object() { [native code] } // Object.prototype是js中的根对象,在它之上没有其他原型了。
console.log(Object.getPrototypeOf(objConstructor.prototype)); // null
原型链的形成
关键就在于让一个对象成为另一个对象的原型。
Object.create(obj):返回一个把指定参数作为原型的新对象。
var obj= {name:"obj"}; var newObj = Object.create(obj); console.log(newObj);
从chrome下的输出我们可以看到 形成了一个三层的原型链 newObject 中的隐式原型属性__proto__指向了obj,obj是使用字面量创建的普通对象,它本质上是使用Object()创建的。所以它指向的是根对象Object.prototype。
原型模式的执行流程
在对象本身查找要访问的属性和方法,如果没有,就到最近的原型上找,直接原型上没有,就到原型的原型上找,找到就直接返回,结束查找。一直找到Object.prototype上都没有,就返回undefined。
原型模式的应用
主要有两种,面向对象写法和任务委托写法。面向对象就是使用原型继承,多态都用的很少,使用起来也比较难理解。任务委托是在《你不知道的js》一书中看到的写法,设计上来看比强行面向对象要直观。但现在时代变了,ES6提供了Class语法糖,让面向对象的js程序可读性较好,便于理解。所以只了解一下任务委托写法。
面向对象
三大特征:封装,继承,多态。
好处:结构清晰,易维护,易拓展。
基于原型继承的面向对象
js中是没有传统面向类语言中的类和构造函数的,它使用new关键字配合普通函数模拟了构造函数,使用原型模拟类,让js拥有了继承和多态的能力。
面向对象写法:定义普通函数做构造调用,在函数的原型上提供实例共享的属性和方法。
任务委托:将数据和工具对象分开定义,让工具对象作为数据对象的原型。达到数据和逻辑分离,将任务处理委托给其他对象的目的。
var util = { say:function(){ console.log(this.name); } } var data = Object.create(util); data.name = "键盘"; data.say(); // 键盘
小结:
1,每个对象都有自己的构造函数,可以通过constructor属性访问到。
2,每个对象都有一个隐式原型属性__proto__,可以使用Object.getPrototypeOf(obj)访问。
3,每个函数都有自己的原型对象,可以通过prototype属性访问。
4,对象的隐式原型属性(__proto__)指向的就是它的构造函数的原型(prototype)。
5,所有使用字面量创建的对象,本质上都是使用相关构造器函数构造出来的,函数是(Function()),数组是(Array())等等。
6,js中的Object是顶层函数它的prototype之上没有其他的原型了。js中所有对象的原型链的顶层几乎都是Object.prototype。
7,原型链就是对象做为其他对象的原型,的同时还有自己的原型。
8,原型模式的执行顺序就是首先在对象自有属性中查找,找不到就问原型有没有,原型没有就问原型的原型有没有,直到找到返回或找到顶层原型后返回undefined。
9,js中的面向对象就是基于原型模式在普通函数的原型上定义共享的属性和方法,达到继承的目的。