JS权威指南第七版阅读拾金

280 阅读13分钟

1.JS解释器启动后或每次浏览器加载新页面时,都会创建一个新的全局对象并且为其添加一组初始的属性,定义了:

  • 全局常量:undefined、Infinity、NaN。
  • 全局函数:isNaN()、pareseInt()、eval()。
  • Date()、RegExp()、String()、Object()、Array()构造函数。
  • Math和JSON这样的全局对象。

2.Node中全局对象为global。

3.浏览器中为window为全局对象。

4.工作线程有self来引用他们的全局对象。

5.2020决定使用globalThis作为引用全局对象的标准形式。

6.使用字面量定义对象和使用new Object定义对象都继承自Object.prototype.

7.Object.create(),创建一个新对象,使用第一个参数作为新对象的原型。

8.客户端JS时间线:

9.全局对象:

10.同源策略:

  • 第一种方法:利用document.domain 设置为一个域名后缀来修改自己的源。
  • 第二种方法:给服务器配置CORS,跨域资源共享,可以让不同源的客户端请求它的数据。

11.XSS:跨站点脚本攻击:

  • 注入恶意代码到URL里面。

  • 解决办法:

  • 处理URL里面的恶意HTML标签。

  • 把不可信的内容显示在标签里面,并且设置sandbox属性设置为禁用脚本和其他能力。

12.使用const把函数表达式赋值给常量,避免重写函数。

13.嵌套函数可以访问包含自己的函数的参数和变量。

14.五种函数调用方法:

  • 作为函数。
  • 作为方法。
  • 作为构造函数。
  • 通过call()或apply()方法间接调用。
  • 通过JavaScript语音特性隐式调用(与常规调用不同)。
  • 条件式调用(函数不是null或者undefined情况下 再调用函数)。

15.递归函数如果多次调用自己,达上万次,就会造成调用栈溢出,内存使用过多。

16.JavaScript语言缺陷,方法中的嵌套函数中的this上下文不是指方法的调用this,而是全局对象或者undefined。可以通过变量保存this值或者定义箭头函数或通过bind来绑定this关键字来解决这个问题。

17.构造函数与普通函数主要区别在于:参数处理、调用上下文、返回值。

18.要注意函数的隐式调用,这些函数可能导致一些bug、副效应、性能问题。19.对象的set和get方法的隐式调用。对象隐式调用toString和valueOf。遍历对象时,也可能造成一系列方法调用。标签模板字面量。代理对象的行为(元编程)。

20.当调用函数时传入的实参少于声明的形参,额外的参数会获得默认值,通常是undefined。

21.要想要只传第二个参数不传第一个参数,必须给第一个参数显示的传递undefined,不能省略。想要省略的参数必须放在参数列表的后面。

22.如果函数有多个形参,则可以使用前面参数的值来定义后面参数的默认值。23.剩余参数可以让我们编写在调用时传入比形参多任意数量的实参的函数,例如Function对象的内置函数call,它就是剩余参数类函数,称为变长函数。它的前面有...,并且只能在形参列表的最后一个。剩余参数在函数体里面始终是数组。可能长度为0。

24.ES6之前的变长函数是通过Arguments对象实现的,但是其效率低,重构代码的时候应该尽量将其替换为剩余参数形式。

25.剩余参数(函数定义时)和扩展运算符,经常同时使用,注意区别。用剩余参数收集多个参数,用扩展运费符将收集的参数给分解开来。

26.函数的实参可以通过解构赋值解析为形参。对于对象来说,左边是属性名,右边是实际的值。

27.JS方法的参数没有预定的类型,在调用传参时也没有类型检查。(TS除外)。可以自己在代码里进行类型检查,提前报错。

28.如果函数使用时需要时常访问一个它自己维护的一个值,可以把这个值定义为这个函数的静态属性。

29.函数体内申明的变量在函数外部不可见。有时,把函数用作临时的命名空间,可以保证定义其中的变量不会污染全局命名空间。

30.可以在把变量定义在函数之中,使之成为局部变量,便不会污染全局变量了。也可以用立即执行函数里面去定义局部变量。相当于就是利用函数作用域作了一个命名空间。在里面定义局部变量。

31.闭包:函数对象与作用域组合起来解析函数变量的机制,称为闭包。

  • 一般形式为嵌套函数闭包。
  • 会捕获自身定义所在外部函数的局部变量及参数的绑定。
  • JS函数的词法作用域规则:使用定义它们的作用域来执行的。
  • 闭包 比 **值定义为这个函数的静态属性(可能被恶意代码改变这个静态属性 导致其出现错误)**这个方法来的安全一些。
  • 闭包可以捕获一次函数调用的局部变量,可以将这些变量作为私有状态。
  • 外部函数可以包含多个嵌套函数。共享私有空间。
  • 闭包就是变着法来给一个函数对象创建私有对象,供里面定义的嵌套函数来引用。
  • 属性获取和设置方法如果受到访问便会立即执行。
  • 闭包也可能会意外共享不该被共享的变量(例如使用var定义的变量 会提升到顶部作用域里面去,这时候所有的闭包函数都共享的这个变量)。
  • 闭包的时候,注意this不是变量而是关键字。可以利用变量赋值,bind、箭头函数来继承this。

32.利用函数的部分应用(柯里化)(部分应用参数),可以实现函数的嵌套调用。但是这样的写法有点复杂,不利于阅读。33.函数记忆:利用闭包创建一个存储输入参数到结果的Map,后面可以直接通过这个Map获得记忆的结果。

34.对象:

  • 对象是可修改的,是按值引用的,不是按值引用的。

  • 自身属性表示为非继承属性。

  • 三种创建方式:字面量、new、Object.creat来创建对象。

  • 通过对象字面量创建的所有对象都有相同的原型对象,是Object.prototype。new创建的对象的原型是构造函数的prototype属性。

  • 原型对象链接起来的序列称为原型链。

  • Object.creat的第一个参数作为新对象的原型。第二个参数可以作为描述新对象的属性。有一个用途是复制一个对象,用于应用而不改变源对象。

  • 点操作符右边必须是一个命名属性的简单标识符,方括号里面必须是一个表达式(其值必须为一个字符串或者可以转为字符串的值)。

  • JS对象是关联数组,可以以数组方式访问其内部的属性。

  • 标识符必须直接书写在JS程序中,它们不是一种数据类型,因此不能被程序操作。

  • 以[]访问属性名时,其中的内容为字符串,可以改变。所以一般动态、不确定属性名的时候用这种方式访问较多。

  • 对象通过其prototype属性创建一个用于继承属性的链条或链表。

  • 如果给对象赋值的属性之前在原型链上面存有,那么原型链上面这个属性将会被隐藏。

  • 几乎所有的对象都有原型,但不是每个对象都有prototype属性(利用代码访问不到其原型)。

  • 查询属性时会用到原型链,而设置属性时不会影响原型链。

  • 属性访问表达式有错误和失败两种情况,访问没有定义的属性,算错误,因为返回undefined。访问对象为null或undefined,那么是失败的,因为这两个值没有属性。

  • 所以尝试设置属性之前,需要提前判断设置对象是否为null或者undefined。

  • let a=b && b.c && b.c.d;

  • ES2020的写法:let a=b?.c?.d。

  • 设置属性失败的情况:

  • 设置只读属性。

  • 设置只读继承属性。

  • 删除属性:

  • 使用delete操作符,操作属性本身。

  • 只会删除对象自身的属性,不会删除对象原型上的属性,除非直接用delete删除原型上的属性。

  • 成功删除、没有影响(删除不存在的值)、非属性访问表达式也会返回true。

  • delete不能删除configurable特效为false的属性、全局环境中定义的变量和函数就是全局的不可配置的属性。

  • 非严格模式下可以省略对全局对象的引用,但严格模式下必须写出完成的属性访问表达式。

  • 测试属性:

  • 测试一个对象是否含有某个属性,用in关键字,如果存在则会返回true。包括 继承 属性。

  • hasOwnProperty:是测试自身的属性,不包含继承的属性 。

  • propertyIsEnumrable(),同时检测了这个属性是否是可枚举的。同时是自有属性。

  • 除了使用in,还可以直接使用!==来检测其是否是未定义的属性。但是!==不能检测存在却被置为undefined的属性。

  • 对象继承的内置方法是不可枚举的。可以使用hasOwnProperty来跳过继承的属性。

  • 有四个方法可以获得属性名数组:

  • Object.keys().返回的是自身的、可枚举的属性名。

  • Object.getOwnPropertyNames.也会返回不可枚举自有的属性。

  • Object.getOwnPropertySymbols.返回名字是符合的自有属性,无论是否可枚举。

  • Reflect.ownKeys(),返回所有属性名。

  • 属性枚举顺序:

  • 先列出名字为非负整数的字符串属性,按照数值从最小到最大。

  • 列出字符串名字的属性。添加到对象的顺序先后,按照在字面量中的顺序。

  • Object.assign()函数用于利用现有对象对另一个对象进行扩展。

  • 第一个参数是目标对象,是要被返回的对象。其他的参数是来源对象,可以从这些来源对象上面继承属性,但是同名属性会按照顺序被覆盖。

  • Object.assign({},defaults,o),先建一个空对象,再用defaluts属性来覆盖到里面,在用o的属性去覆盖,这样就可以得到o的属性和默认属性的对象了。

  • 使用扩展运算符也可以,o=Object.assign(...defaults,...0);

  • 也可以自己写一个函数,只复制那些没有的属性。

  • 函数JSON.stringify()和JSON.parse()用于序列化和恢复JS对象。

  • JSON语法是JS语法的子集。不能表示所有的JS值。

  • 只序列化可枚举的属性。

  • 可接受第二个参数进行自定义序列化和恢复。

  • Object.prototype上面定义的方法:

  • toString。

  • toLocalString。本地惯例格式化数值、日期、时间等。

  • valueOf。转换为数值

  • toJSon。会被JSON.stringify()调用。

  • 很多内置对象自定义了上述方法。

  • 当属性名和变量名相同时,可以省略变量名和分号。

  • 可以通过计算属性作为对象的属性,可以输入一个表达式,其求值后转换为字符串作为属性名。

  • 符号(Symbol 唯一的声明),可作为对象的属性值,symbol是原始值,不是对象,所以不是用new来声明一个 symbol。直接用工厂函数的模式进行声明。使用两个不同的参数声明的符号也是不同的符号。主要用途是扩展对象属性的安全机制,防止新定义的属性和原来定义的属性重复。但是也可被Object.getOwnPropertySymbols()给找到。一般来说,symbol定义的属性是不可迭代的,但是为它定义一个方法即可迭代。

  • 对象里面定义变量为属性,加上[],如O{[a]:"a"}。

  • 扩展运算符(...),用在对象的上下文中,复制对象的属性。扩展运算符只扩展自身属性,不扩展继承来的属性。谨慎使用,工作量取决于对象的属性多少。

  • 对象还可以定义set、get方法来在定义属性或者获取属性时触发调用。方法名就是属性名。两种方法可以继承。

35.类:

  • 如果多个对象共享一些属性,为这些对象定义一个类。

  • 类基于原型的继承。多个对象从同一个原型继承属性,即都是这个类的实例。

  • 其他语言可能是基于类的继承,而JS是基于原型的继承。

  • 利用工厂函数(使用create来实现继承)去定义一个类。

  • 生成器函数和迭代器是为了这个类与for/of和扩展运算符一起使用。

  • 只有函数对象才有prototype属性。用于作为构造函数给实例继承。被bind函数绑定生成的函数没有prototype属性,调用时可以使用底层函数的prototype属性。

  • 使用构造函数去定义一个类,使用this关键字来定义属性。直接使用prototype来定义继承的属性和方法。大写字母开头。新对象是调用构造函数之前自动创建的。把构造函数当作这个新对象的方法来调用,通过this来引用。

  • new.target 可以检测该函数是否作为构造函数被调用。

  • 箭头函数没有prototype属性,不可作为构造函数。

  • 使用instanceof来检测对象和某一类的继承关系,跨代也可以。检查是否继承了该类的prototype属性。但不能检测使用的是哪个构造函数。

  • 还可以使用isPrototypeOf()方法。检测原型。

  • constructor的值就是函数对象。

  • 使用.引用的方法重写prototype对象,即可自动添加constructor属性,字面量定义则需要手动反向引用constructor。

  •  类(class)里面使用constructor定义构造函数,类体里面默认使用严格模式,并且以申明式声明的类不具有提升效果。

  • 静态方法是直接定义在类名上的,也就是定义在构造函数上面的。

  • 类中定义字段的方案正在优化。私有和公有字段。

  • 为已有类添加方法:直接在原型上面调加即可。即可增强类。但是不建议这样做。

  • 子类:

  • 子类构造函数必须调用父类构造函数,才能完全初始化子类实例。

  • 就是通过prototype将父类的prototype赋予给子类的prototype.es6之前

  • es6用extends和super创建子类。