关于this

匿名函数无法从函数内部引用自身
this既不指向函数自身也不指向函数的词法作用域。this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用

this全面解析

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

this词法

箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。
箭头函数最常用于回调函数中,例如事件处理器或者定时器

对象

语法

对象可以通过两种形式定义:声明(文字)形式和构造形式
构造形式和文字形式生成的对象是是一样的。唯一的区别是,在文字声明中你可以添加多个键/值对,但是在构造形式中你必须逐个添加属性
存储在对象容器内部的是这些属性的民称,他们就像指针(引用),指向这些真正的存储位置。使用.操作符(属性访问)或者[]操作符(键访问)
.操作符要求属性名满足标识符的命名规范,而[]语法可以接受任意字符串作为属性名。
在对象中属性名永远都是字符串,会自动转换为字符串

可计算属性名

可计算属性名:可以再文字形式中使用[] 包裹一个表达式来当做属性名

属性和方法

无论返回值是什么类型,每次访问对象的属性就是属性访问。如果属性访问返回的是一个函数,那它也并不是一个方法。

数组

数组也是对象,每个下标都为整数,可以为数组添加不同的属性
注意:如皋港在数组中添加一个属性,属性名是一个数字,那它会变成一个数值下标

属性描述符

使用Object.defineProperty(...)来添加一个新属性或者修改一个已有属性

  • Writable:决定是否可以修改属性的值
  • Configurable:只要属性是可配置的,就可以使用defineProperty(...)方法来修改属性描述符
    注意:即使属性是configurable:false,我们还是可以把writable的状态由true修改为false,但是无法由false改为true
  • 不变性
    • 对象常量

结合writable:falseconfigurable:false就可以创建一个真正的常量属性(不可修改、重定义或者删除)

  • 禁止扩展

如果想要禁止一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions(...)

  • 密封

Object.seal(...)会创建一个”密封”的对象,这个方法实际上会在一个现有对象上调用Object.preventExtensions(...)并把所有现有属性标记为configurable:false密封之后不仅不能添加新属性,也不能重新配置或者删除任何现有属性(可以修改属性的值)

  • 冻结

Object.freeze(...)会创建一个冻结对象,这个方法实际上会在一个现有对象上调用Object.seal(...)并把所有”数据访问”属性标记为writable:false,这样就无法修改它们的值。
它会禁止对于对象本身及其任意直接属性的修改
注意:这个对象引用的其他对象是不受影响的

存在性

  • in 操作符会检查属性是否在对象及其[[Prototype]]原型链中
    in 操作符可以检查容器内是否有某个值,实际上检查的是某个属性名是否存在
  • hasOwnProperty(...)只会检查属性是否在对象中,不会检查[[Prototype]]
  • 枚举

    • propertyIsEnumerable(...)会检查给定的属性名是否直接存在于对象中(而不是在原型链上)并且满足enumerable:true
    • Object.keys(...)会返回一个数组,包含所有可枚举属性。Object.getOwnPropertyNames(...)会返回一个数组,包含所有属性,无论它们是否可枚举
    • inhasOwnProperty(...)的区别在于是否查找[[Prototype]]链,Object.keys(...)Objet.getOwnPropertyNames(...)只会查找对象直接包含的属性

      遍历

  • for...in可以用来遍历对象的可枚举性列表(包括[[Prototype]]链)

  • forEach(...)会遍历数组中的所有值并忽略回调函数的返回值
  • every(...)会一直运行知道回调函数返回false(或者”假”值)
  • some(...)会一直运行直到回调函数返回true(或者”真”值)
  • for...of 首先会向被访问对象请求一个迭代器对象,然后通过调用迭代器对象的next()方法遍历所有返回值

注意:遍历数组下标时采用的是数字顺序(for循环或其他迭代器),但是遍历对象属性时的顺序是不确定的,在不同的JavaScript引擎中可能不一样

混合对象-类

构造函数

类实例是由一个特殊的类方法构造的,这个方法名通常和类名相同,被称为构造函数。这个方法的任务就是初始化实例需要的所有信息(状态)

多态

super 函数是”超类”,表示当前类的父类/祖先类

原型

所有普通的[[Prototype]]链最终都会指向内置的Object.prototype

关于名称

在JavaScript中,我们并不会将一个对象(“类”)复制到另一个对象(“实例”),只是将它们关联起来。从视觉角度来说,[[Prototype]]机制如下图所示,箭头从右到左,从下到上:
image.png

继承意味着复制操作,JavaScript(默认)并不会复制对象属性。相关,JavaScript会在两个对象之间创建一个关联,这样一个对象就可以通过委托访问另一个对象的属性和函数。
还有个偶尔会用到的JavaScript术语”差异继承”,基本原则就是在描述对象行为时,使用其不同于普遍描述的特质。举例来说,描述汽车时你会说汽车是有四个轮子的一种交通工具,但是你不会重复描述交通工具具备的通用特性(比如引擎)

属性.constructor引用的是对象关联的函数

构造函数

按照JavaScript世界的惯例,”类”名首字母要大写,所以名字协作Foo而非foo
在一个普通函数调用前面加上new关键字之后,就会把这个函数调用变成一个”构造函数调用”。实际上,new会劫持所有普通函数并用构造对象的形式来调用它,换句话说,在JavaScript中对于”构造函数”最准确的解释是,所有带new的函数调用。函数不是构造函数,但是当且仅当使用new时,函数调用会变成”构造函数调用”
对象的.constructor会默认指向一个函数,这个函数可以通过对象的.prototype引用

对象关联

[[Prototype]]机制就是存在于对象中的一个内部链接,它会引用其他对象。通常来说,这个链接的作用是:如果在对象上没有找到需要的属性或者方法引用,引擎就会继续在[[Prototype]]关联的对象上进行查找。同理,如果在后者也没有找到需要的引用就会继续查找它的[[Prototype]],以此类推。这一系列对象的链接被称为”原型链”

创建关联

Object.create(...)会创建一个新对象,并把它关联到我们指定的对象(foo),这样我们就可以充分发挥[[Prototype]]机制的威力(委托),并且避免不必要的麻烦(比如使用new的构造函数调用会生成.prototype.constructor引用)