Web前端的知识之旅哟——对象的枚举与this

命名空间

在进入正文之前,我们先提一下命名空间的概念。

由于我们的一个项目是很多人开发的,每个人都有一套自己的变量,为了防止人和人之间变量的互相干扰,我们经常把自己的变量都放到相对应的模块里面,模块和模块之间是没有关系的,这样就算变量名相同也不会互相干扰。我们经常用对象来实现这种模块的作用。


1. var spacename = {
2.       department1: {
3.            person1: {},
4.            person2: {},
5.            ……
6.       },
7.       department2: {
8.            person1: {},
9.            person2: {},
10.            ……
11.       }
12.       ……
13. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

对象的枚举

在介绍对象的枚举方法之前,我们要理解一个概念。

查看对象属性我们知道可以用obj.name这样的点操作符来查看,但是我们还有一种其他的方式:

obj[‘name’]

这种方法类似数组的查看,但其实事实是数组模仿了对象的查看方式。

这种方法是在系统底层里面的查看对象属性的写法,我们常用的点操作符obj.name在系统底层其实也转化成了obj[‘name’]这种形式。

下面来介绍一下对象的枚举方法:

for-in操作符

我们知道要枚举一个数组的所有元素,只要用一个for循环从头到尾遍历一遍就可以了。

但是对象并不能用for循环来遍历属性,所以这里我们就要使用for-in操作了。


1. var obj = {
2.       name: ‘scarlett’,
3.       age: 18,
4.       sex: ‘female’
5. }
6. for(var prop in obj) {
7.       console.log(prop + ‘: ‘ + obj[prop]);
8. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

我们for-in循环会按照属性的顺序取出属性名然后 赋给prop,所以我们打印的prop都是属性名,obj[prop]则是相对应的属性的值

注意:这里我们不能写成obj.prop的形式,因为在系统底层会转化成obj[‘prop’]的形式,但是我们并没有prop这个属性,它应该是一个变量,所以会打印出来undefined,这里我们必须使用obj[prop]来调用

在es3和es5的非严格模式下,for-in循环都会把原型里面的属性一起打印出来,es5的严格模式不会。

下面我们来介绍三种操作符:

1.hasOwnProperty

这个操作符的作用是查看当前这个属性是不是对象自身的属性,在原型链上的属性则会被过滤掉。如果是自身的,就返回true,否则返回false。


1. function Person () {
2.       this.name = ‘scarlett’
3. }
4. Person.prototype = {
5.       age:18
6. }
7. var oPerson = new Person();
8. for(var prop in oPerson) {
9.       if (oPerson.hasOwnProperty(prop)) {
10.             console.log(oPerson[prop];
11.       }
12. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

这样,我们的for-in循环就会只打印自身的name属性而不会去打印原型上的age属性。

2.in操作符

这个操作符的作用是查看一个属性是不是在这个对象或者它的原型里面。


1. ‘name’ in oPerson; // true
2. ‘age’ in oPerson; // true
3. ‘sex’ in oPerson; // false
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

3.instanceof操作符

这个操作符的作用是查看前面的对象是不是后面的构造函数构造出来的,和constructor挺像的。


1. oPerson instanceof Person; // true
2. oPerson instanceof Object; // true
3. {} instanceof Object; // true
4. {} instanceof Person; //false
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

也可以理解为:后面的构造器的原型是否在前面对象的原型链上

this

1.预编译过程中 this–>window

2.全局作用域里面 this–>window

3.call/apply可以改变this指向

4.obj.func() func()里面的this指向obj


1. var obj = {
2.       height:190,
3.       eat: function () {
4.             this.height++;//eat在没有执行之前,谁也不知道this指向谁
5.       }
6. }
7. obj.eat();//后面的方法,谁调用它,里面的this就指向谁。
8. eat.call(obj);//eat的this指向obj
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

这一些就是name的全部知识点,当我们能够理解下面这一段样例代码中的this的指向问题了,那么大家应该就掌握了this的所有问题了。


1. var name = ‘222’;
2. var a = {
3.       name:‘111’,
4.       say:function () {
5.            console.log(this.name);
6.       }
7. }
8.  
9. var fun = a.say;
10. fun(); 
11. a.say(); 
12. var b = {
13. name: ‘333’,
14. say: function (fun) {
15.             fun();
16.       }
17. }
18.  
19. b.say(a.say); 
20. this–>b;
21. fun();预编译过程 fun里面的this指向window
22. b.say = a.say;
23. b.say();
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

第一处打印调用fun(),这里其实就是把a.say这个函数的函数体赋给了fun这个函数,相当于在全局空间下写了一个func函数,里面的代码就是a.say里面的代码,所以此时的this指向window,因此打印222。

第二处打印调用了a.say(),按照我们前面说的谁调用函数,函数里面的this就指向谁,因此这里的this指向a,所以打印111。

第三处比较复杂,它调用了b.say(a.say))。这里其实是吧a.say这个函数体替换了原本b中fun的地方,我们在调用b.say()这个方法的时候,里面的this是指向b的,但是这个this并不在fun里面而是在say里面


1. say: function (fun) {
2.       this;// b
3.       fun();
4. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

其实的结构应该是这样的,所以fun里面的this和我们所想的指向b的那个this不是一个东西,fun里面的this是在预编译阶段的指向window的,因此这里也打印222。其实和第一种直接在全局调用a.say的函数体方法差不多。

第四个b.say = a.say; b.say();其实也第二种一模一样的意思,不过此时的this指向b而已,因此打印333。


1. var name = ‘222’;
2. var a = {
3.       name:‘111’,
4.       say:function () {
5.            console.log(this.name);
6.       }
7. }
8. var fun = a.say;
9. fun();  // 222
10. a.say();  // 111
11. var b = {
12. name: ‘333’,
13. say: function (fun) {
14.             fun();
15.       }
16. }
17. b.say(a.say);  // 222
18. this–>b;
19. fun();预编译过程 fun里面的this指向window
20. b.say = a.say;
21. b.say(); // 333
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)