this的全面解析
前提就是调用位置:调用位置就是函数在代码中的代用位置(而不是声明的位置)。
this的绑定规则
- 默认绑定:最常用的函数调用类型:独立函数调用,可以把这条规则看作是无法应用其他规则时的默认规则。
foo(); bar(); baz(); 在浏览器环境下this指向全局对象,但是在严格模式下不能将全局对象用于默认绑定,因此this会绑定到undefined;通常来说strict与非strict不能混用。 - 隐式绑定:考虑调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。
obj.foo();此时调用foo函数时this就会绑定为obj对象,因此this.a和obj.a是一样的。对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。 obj1.obj2.foo(),只有obj2起作用。
- 隐式丢失:虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此此处的this绑定为默认绑定。更常见的情况是回调函数中,参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值。
var bar = obj.foo;
bar()
- 显示绑定:JavaScript提供的绝大多数函数以及自己创建的函数都可以使用
call(...)和apply(...)方法。第一个参数是一个对象是给this准备的,区别在于第二个参数call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组,在函数调用时就会将this绑定为这个对象。
- 硬绑定:为了解决绑定丢失的问题,ES5提供了内置的方法
Funcation.prototype.bind()
- new绑定:JavaScript中,构造函数只是一些使用new操作符时被调用的函数,构造函数就是普通函数只是当使用new操作符调用时被称为构造函数调用,只是对于函数的“构造调用”。当不使用new操作符调用时就是一个普通函数。
- this的绑定优先级:new绑定>显示绑定>隐式绑定>默认绑定
- 箭头函数:箭头函数并不是使用function关键字定义的,而是使用被称为“胖箭头”的操作符=>定义的,箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。
属性描述符
Object.getOwnPropertyDescriptor(myObject, "a"); // 获取属性a的属性描述符信息{}
使用defineProperty(...)给对象添加属性并显示的指定一些特性,
Writable:决定是否可以修改属性的值,默认值为true;Configurable:决定属性是否可以通过defineproperty(...)方法来修改属性描述符,默认为true;Configurable修改成false是单向操作,无法撤销,即便Configurable:false; 我们依然可以把writable的状态由true改为false但是无法由false改为true。同时Configurable:false;还会禁止删除这个属性操作。delete就是一个删除对象属性的操作。enumerable:是否可枚举,默认为true;value:存储值;set:setter是一个隐藏的函数,会在设置属性值的时候调用;get:getter也是一个隐藏的函数,会在获取属性值的时候调用;- 数据描述符:value、Writable、Configurable、enumerable
- 访问描述符:get、set、Configurable、enumerable
不变性
- 对象常量:结合Writable:false和Configurable:false;可以创建一个真正的常量属性(不可以修改、重定义或者删除)
- 禁止扩展:禁止一个对象添加新属性并且保留己有属性,
Object.preventExtensions(...); - 密封:不能添加新属性,也不能重新配置或者删除任何现有属性(但是可以修改属性的值);
Object.seal(...);此方法实际上是会先调用Object.preventExtensions(...)方法并把所有现有属性标记为Configurable:false; - 冻结:
Object.freeze(...);实际上是先调用Object.seal(...)方法,并把所有数据访问属性标记为Writable:false;这样就无法修改他们的值了。但是这个对象引用的对象并不受影响。
判断对象中是存在这个属性
in操作符:("a" in myObject); //返回布尔值;判断对象本身及其原型链上是否存在该属性。hasOwnProperty:myObject.hasOwnProperty("a") //返回布尔值;只检察对象本身上是否有该属性(不包括原型链)。- in操作符可以检察容器中是否存在某个值,但是实际上是检察的某个属性名是否存在。对于数组来说很重要,属性名为索引。
判断属性是否可以枚举
myObject.propertyIsEnumerable(...):判断某个属性名是否直接存在于对象上(而不是原型链上)并且满足enumerable:true;Object.keys(...):返回一个数组,包含所有可枚举的属性(不包括原型链);Object.getOwnPropertyNames(...):会返回一个属性名组成的数组,包含所有属性无论是否可以枚举(不包括原型链);
遍历
- for...in循环可以用来遍历对象的可枚举属性列表(包括原型链);
Object.keys():方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。(不包括原型链)Object.values():该方法用来遍历对象自身存在的属性(不包括原型链)
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.values(an_obj)); // ['b', 'c', 'a']
- for循环数组;
- for...of循环数组,循环的结果为值并非索引,(如果对象本身定义了迭代器也可以用来遍历对象,如果没有定义迭代器直接遍历对象会报错:..is not iterable);实质上是首先会向被访问的对象请求一个迭代器对象,然后通过迭代器对象的next()方法来遍历所有返回值。
- ES5新增了一些数组的辅助迭代器,
- forEach(...)
- every(...):会一直运行直到会回调函数返回false
- some(...):会一直运行直到回调函数返回true