JavaScript 组件化开发之路(一)
JavaScript中的对象属性
深入了解JavaScript中的对象以及对象中的属性,是构建可复用组件的前提。
对象的创建方式,常用的有两种:
-
通过对象字面量创建
var obj = {x:1}
-
通过构造函数创建
var obj = new Object()
我们可以用for-in来遍历并输出对象的属性。
var obj = { x: 1, y: 2, vars:['a','b','c'], add: function(){return this.x+this.y} } for(var prop in obj){ console.log(prop) } //输出 x, y, vars,add
在组件的开发中,我们经常要用到对象的继承,现在我们创建一个对象,它继承自obj,用es5里的方法,可以很容易的实现
var obj2 = Object.create(obj) for(var prop in obj2){ console.log(prop) } //输出 x, y, vars,add
可以看到,obj2并没有自己的实例属性,但是,我们用for-in仍然可以得到三个属性。这三个属性继承自obj。 由于es5的这个方法并没有得到所有浏览器的支持,我们需要尝试自己实现对象的继承,去更加深入的了解继承的原理。
var inherit = function(obj){ function F(){} F.prototype = obj return new F() } var obj3 = inherit(obj) for(var prop in obj3){ console.log(prop) } //x, y, vars,add
看上去不错,我们也实现了所谓的继承,但是觉得似乎哪里有点不对劲?我们再创建一个对象试试
var obj4 = inherit(obj) for(var prop in obj4){ console.log(prop) } //x,y.vars,add
我们试着操作obj3的属性
obj3.x=0 console.log(obj3.x) //0 console.log(obj4.x) //1
貌似没什么问题,继续
obj.vars.push('d') console.log(obj3.vars) //["a", "b", "c", "d"] console.log(obj4.vars) //["a", "b", "c", "d"]
出大事了。对一个对象的修改,影响到了另外一个对象。组件设计中,如果一个组件的修改,会影响到同类的其它实例,那还有什么独立性可言? 发生这种结果的原因, 就在我们实现的inherit方法中。在这个实现继承的方法中,我们首先创建了一个新的构造函数。这个构造函数的prototype指向了我们要继承的对象。因此,下面调用这个构造函数产生的对象,原型就是obj。当我们调用obj3.vars时,obj3本身并没有vars这个属性,于是它顺着原型链向上查找,在obj中找到了vars这个属性。这个属性的值是一个数组,数组属于引用类型,也就是说obj.vars属性中保存的只是一个指针(地址)。我们对obj3.vars得到的也是这个指针。针对这个指针指向的数组,进行了push操作,结果就是改变了这个指针指向的数组的值。当我们读取obj4的vars属性时,由于obj4本身也没有这个属性,它也会顺着原型链向上查找,找到obj.vars,而这时的obj.vars指向的数组已经被obj3的操作改变了。
这种继承方式显然无法满足我们的需求。我们希望每个继承而来的对象,在享有共有属性和方法的同时,也有自己私有的一些属性和方法。这些私有的属性不应该被共享,也不可以被别的对象修改。
下面是另一个实现继承的方式
var myClass = function(){ this.vars = ['a','b','c'] this.x = 1 } myClass.prototype.y=1 myClass.prototype.say=function(){console.log('hi')} myClass.prototype.publicArr=[1,2,3] var my1 = new myClass var my2 = new myClass my1.vars.push('d') console.log(my1.vars) // ['a','b','c','d'] console.log(my2.vars) // ['a','b','c']
我们把每个对象的私有属性,定义在构造函数内部,而把那些需要共同享用的属性,放在构造函数的prototype属性中。这样,当我们创建对象时,每个对象都会拥有自己的私有属性(vars,x),也可以通过原型链,查找到原型中的属性。
console.log(my1.y) //1 my1.say() //'hi' console.log(my2.y) //2 my2.say() //'hi' //共享的属性,可以被其它的对象改变 my1.publicArr.push(4) console.log(my1.publicArr) //[1, 2, 3, 4] console.log(my2.publicArr) //[1, 2, 3, 4]