在使用for-in循环时,返回的是所有能够通过对象访问的,可枚举的属性,其中既包括在实例中的属性,也包括在原型中的属性;如果在IE8-下屏蔽了原型中已有的方法,那么在IE8-下不会有任何反应;如下代码:
var obj = { toString: function(){ return "aa"; } }; for(var i in obj){ if(i == "toString") { alert(1); } }
如果我把上面的toString改成toString22的话,就可以在IE下打印出1,否则没有任何执行,那是因为他屏蔽了原型中不可枚举属性的实例属性不会在for-in循环中返回,因为原型中也有toString这个方法,在IE中,由于其实现认为原型的toString()方法被打上了值为false 的[[Enumerable]]标记,因此应该跳过该属性,结果我们就不会看到警告框。
要取得对象上所有可枚举的实例属性,可以使用ECMAScript 5 的Object.keys()方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
如下代码演示:
function Dog() {}; Dog.prototype = { name: 'wangwang', age:'11', say: function(){ alert(this.name); //wangwang } } var dog1 = new Dog() var keys = Object.keys(Dog.prototype); console.log(keys);//["name",'age','say']
如上代码;keys将保存为一个数组,这个顺序是在for-in出现的顺序,如果我们想要得到所有实例属性,无论它是否可枚举,我们可以使用 Object.getOwnPropertyNames()方法,如下代码:
function Dog() {}; Dog.prototype = { name: 'wangwang', age:'11', say: function(){ alert(this.name); //wangwang } } var dog1 = new Dog(); var keys = Object.getOwnPropertyNames(Dog.prototype); console.log(keys);//["name",'age','say']
Object.keys()和Object.getOwnProperty-Names()方法都可以用来替代for-in 循环。支持这两个方法的浏览器有IE9+、Firefox 4+、Safari 5+、Opera
12+和Chrome。
我们接下来再来理解下原型对象的概念;如下代码:
function Dog() {}; Dog.prototype = { name: 'wangwang', age:'11', say: function(){ alert(this.name); //wangwang } } var dog1 = new Dog(); console.log(dog1 instanceof Object); // true console.log(dog1 instanceof Dog); // true console.log(dog1.constructor == Dog); // false console.log(dog1.constructor == Object); // true
上面的第三行为什么会打印false呢?我们知道,每创建一个函数,就会同时创建它的prototype对象。这个对象会自动获得constructor属性,我们实例化一个对象的时候,那是因为我们没有给他指定constructor属性,默认情况下它会重写prototype对象,因此constructor属性也就变成了新对象的constructor属性了,不再指向Dog函数,如果我们需要让他还是指向与Dog函数的话,我们可以在Dog.property中添加constructor属性,如下代码:
function Dog() {}; Dog.prototype = { constructor: Dog, name: 'wangwang', age:'11', say: function(){ alert(this.name); //wangwang } } var dog1 = new Dog(); console.log(dog1 instanceof Object); // true console.log(dog1 instanceof Dog); // true console.log(dog1.constructor == Dog); // true
理解原型的动态性
比如如下代码:
function Dog() {}; var dog1 = new Dog(); Dog.prototype.say = function(){ alert(1); } dog1.say(); // 1
我们先实例化一个对象后,再给原型添加一个say方法,再我们使用实例调用该方法的时候也可以调用的到,这也就是说,实例会先搜索该say方法,如果没有搜索到,那么它会到原型里面去搜索该方法,如果能查找的到就执行,否则就会报错,没有这个方法;
虽然可以随时为原型添加方法和属性,且修改的方法和属性能从实例中表现出来,但是如果重写整个原型方法那就不行了;如下代码:
function Dog() {}; var dog1 = new Dog(); Dog.prototype = { constructor: 'Dog', name:'dog', age:'1', say: function(){ alert(this.age); } } dog1.say(); // 报错 var dog1 = new Dog();
实例化一个对象时,会为该实例指向原型的指针,但是如果重写该原型的话,那么就会把该对象与原来的那个原型切断关系,那么继续调用该方法就会调用不到,如上面的代码,如果我再在重写该原型下面继续实例化该对象Dog,继续调用say方法就正常了;如下代码:
function Dog() {}; var dog1 = new Dog(); Dog.prototype = { constructor: 'Dog', name:'dog', age:'1', say: function(){ alert(this.age); } } //dog1.say(); // 报错 var dog2 = new Dog(); dog2.say(); // 1
理解原型重写
我们从上面可知,原型是可以被重写的,那么原型重写后造成的问题就是会改变之前的实例指针指向原来的原型,那也就是说之前的原型假如有继承等操作的话,通过重写后的原型也会改变,所以在实际操作的时候要小心点,原型重写可以使同一个构造器实例出2个不同的实例出来;如下代码: