this全面解析+对象属性描述符

122 阅读5分钟

this的全面解析

前提就是调用位置:调用位置就是函数在代码中的代用位置(而不是声明的位置)。

this的绑定规则

  1. 默认绑定:最常用的函数调用类型:独立函数调用,可以把这条规则看作是无法应用其他规则时的默认规则。foo(); bar(); baz(); 在浏览器环境下this指向全局对象,但是在严格模式下不能将全局对象用于默认绑定,因此this会绑定到undefined;通常来说strict与非strict不能混用。
  2. 隐式绑定:考虑调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。obj.foo();此时调用foo函数时this就会绑定为obj对象,因此this.aobj.a是一样的。对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。 obj1.obj2.foo(),只有obj2起作用。
  • 隐式丢失:虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此此处的this绑定为默认绑定。更常见的情况是回调函数中,参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值。
 var bar = obj.foo;
 bar()
  1. 显示绑定:JavaScript提供的绝大多数函数以及自己创建的函数都可以使用call(...)apply(...)方法。第一个参数是一个对象是给this准备的,区别在于第二个参数call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组,在函数调用时就会将this绑定为这个对象。
  • 硬绑定:为了解决绑定丢失的问题,ES5提供了内置的方法Funcation.prototype.bind()
  1. 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

不变性

  1. 对象常量:结合Writable:false和Configurable:false;可以创建一个真正的常量属性(不可以修改、重定义或者删除)
  2. 禁止扩展:禁止一个对象添加新属性并且保留己有属性,Object.preventExtensions(...);
  3. 密封:不能添加新属性,也不能重新配置或者删除任何现有属性(但是可以修改属性的值);Object.seal(...);此方法实际上是会先调用Object.preventExtensions(...)方法并把所有现有属性标记为Configurable:false;
  4. 冻结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