JS是门面向对象语言,其对象是用prototype属性来模拟的,下面例举下常用的封装方式。
常规封装
1 function Person(name, age, sex){ 2 this.name = name; 3 this.age = age; 4 this.sex = sex; 5 } 6 7 Person.prototype = { 8 constructor: Person, 9 sayHello: function(){ 10 console.log("hello"); 11 } 12 }
Person函数的职责是构造对象,如果把初始化的东西也放在里面,代码就会显得繁琐,于是有了升级版
升级版
function Person(info){ this._init(info) } Person.prototype = { constructor: Person, _init: function(info) { this.name = info.name; this.age = info.age; this.sex = info.sex; } sayHello: function(){ console.log("hello"); } }
可是name, age, sex并没有在Person里申明呀?哪里来的?
new的执行原理
new的执行过程可以用下面的函数来表示
var myNew = function(constructor, args){ var o = {}; o.__proto__ = constructor.prototype; var res = constructor.apply(o,args); var type = typeof res; if(['string','number','boolean','null','undefined'].indexof(type) != -1) { return o; } return res; }
o.__proto__ = constructor.prototype;这句是将构造函数的原型属性prototype赋值给o的原型对象__proto__.如此在调用this._init(info)时,对象o就可以在其原型对象中查找_init方法了(原型链).
后面这句是重点了
var res = constructor.apply(o,args),以o为上下文调用函数,同时将参数作为数组传递,那么this._init(info)这句会被o执行,函数
_init: function(info) { this.name = info.name; this.age = info.age; this.sex = info.sex; }
以o为上下文调用,o也将拥有自己的name, age, sex属性。
最后构造函数中,return复合类型,包括对象,函数和正则表达式,那么会直接返回这个对象,否则返回o。
类jQuery封装
jQuery对象具有很强的集成性,可以作为函数调用,也可以作为对象调用,当作为函数调用的时候,无需new而返回一个实例。
var Person = function(info) { return new Person.prototype.init(info); } Person.prototype = { constructor: Person, init: function(){ this.name = info.name; } } Person.prototype.init.prototype = Person.prototype;
这种封装将对象的构造放在函数里,自己充当一个工厂。不断调用prototype并不是一个直观的做法,于是
Person.fn = Person.prototype = { constructor: Person, init: function(){ this.name = info.name; this.sayHello =.function(){ this.makeArray(); } }, makeArray: function (){ console.log(this.name); } } Person.fn.init.prototype = Person.prototype;
最后用闭包封装起来
var Person = (function(window){ var Person = function(name) { return new Person.fn.init(name); } Person.prototype = Person.fn = { constructor: Person, init: function(name) { this.name = name; this.sayHello = function () { this.makeArray(); } }, makeArray: function(){ console.log(this.name); } } Person.fn.init.prototype = Person.fn; return Person; })()
最后介绍一种object.create()构造对象的方式,可以传递一个对象Person,构造一个p,并且使p继承Person
Person = { constructor: Person, sayHello: function(){ console.log("hello"); } } var p = Object.create(Person); console.log(p); p.sayHello();
可以看到对象Person 的属性成了p的原型属性,也就是p继承了Person。
我们可以实现Object.create()
Object.create = function(prototype) { function Func () {} Func.prototype = prototype; var o = new Func; return o; }
这里将Person最为构造函数的原型属性,就可以构造出以Person为原型对象的对象。