《你不知道的JavaScript》阅读笔记

朱治龙
2022-02-04 / 0 评论 / 13 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2022年02月05日,已超过1067天没有更新,若内容或图片失效,请留言反馈。

判断this

现在我们可以根据优先级来判断函数在某个调用位置应用的是哪条规则。可以按照下面的顺序来进行判断:

1、函数是否在new中调用(new 绑定) ?如果是的话 this 绑定的是新创建的对象。

var bar = new foo()

2、函数是否通过call、apply (显式绑定)或者硬绑定调用?如果是的话,this 绑定的是
指定的对象。

var bar = foo.call(obj2)

3、函数是否在某个上下文对象中调用(隐式绑定) ?如果是的话,this 绑定的是那个上
下文对象。

var bar = obj1.foo()

4、如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定
到全局对象。

var bar = foo()

可计算属性名

ES6中增加了可计算属性名,可以在文字形式中使用[]包裹一个表达式来当作属性名:

var prefix = 'zhuzl'
var content = {
  [prefix + '_name']: 'zhuzl',
  [prefix + '_age']: 34
}
content['zhuzl_name'] // zhuzl

属性描述符

从ES5开始,所有的属性都具备了属性描述符。

var myObject = {}
Object.defineProperty(myObject, 'a', {
  value: 2,
  writable: true, // 是否可以修改值
  configurable: true, // 属性描述符是否可配置,该值为单向操作,无法撤消。即便该值为false,还是可以把writable修改为false,但是无法改为true。若值为false,该值不可delete
  enumerable: true // 控制该属性是否出现在对象的属性枚举列表中。比如说for...in 循环
})

对象不变性

  • 对象常量
    结合writable:false和configurable:false可以创建一个真正的常量属性(不可修改、重定义和删除)
var myObject = {}
Object.defineObject(myObject, 'FAVORITE_NUMBER', {
  value: 42,
  writable: false,
  configurable: false
})
  • 禁止扩展
    如果想禁止一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions(...)
  • 密封
    Object.seal(...) 会创建一个“密封”的对象,这个方法实际上会调用Object.preventExtensions(...) 并把所有现有属性标记为 configurable: false。所以密封之后不仅不能添加新属性,也不能重新配置或删除任何现有属性。
  • 冻结
    Object.freeze(...)会创建一个冻结对象,这个方法实际上会在一个对象上调用Object.seal(...)并把所有“数据访问”属性标记为writable: false。这个方法是应用在对象上的级别最高的不可变性。

setter 和 getter

  • 通常getter 和 setter 是成对出现的,只定义一个的话通常会产生意料之外的行为。
var myObject = {}
Object.defineProperty(myObject, 'b', {
  get: function () {},
  set: function () {}
})
// OR
var myObject = {
  get a() {
    return this._a_
  },
  set a(val) {
    this._a_ = val * 2
  }
}

遍历

  • for(var k in obj):用来遍历对象的可枚举属性列表,包括prototype原型链上的属性。
  • for(var v of arr):用于遍历数组的值,理论上是通过调用数组内置的@@iterator迭代器对象,然后通过调用迭代器对象的next()方法来手动遍历数组,原理如下:
var myArray = [1, 2, 3]
for (var v of myArray) {
  console.log(v)
}
// 1
// 2
// 3

// ----实际工作模式----
var myArray = [1, 2, 3]
var it = myArray[Symbol.iterator]()
it.next() // {value: 1, done: false}
it.next() // {value: 2, done: false}
it.next() // {value: 3, done: false}
it.next() // {done: true}
  • 数组forEach():会遍历数组中的所有值并忽略函数的返回值
  • 数组every():会一直运行直到回调函数返回false(或“假”值)
  • 数组some():会一直运行直到回调函数返回true(或“真”值)

原型继承

function Foo(name) {
  this.name = name
}
Foo.prototype.myName = function () {
  return this.name
}
function Bar (name, label) {
  Foo.call(this, name)
  this.label = label
}
// 我们创建了一个新的Bar.prototype对象并关联到Foo.prototype
// 注意:现在没有Bar.prototype.constructor 了,如果需要这个属性的话可以手工修复一下
Bar.prototype = Object.create(Foo.prototype)

Bar.prototype.myLabel = function () {
  return this.label
}
var a = new Bar('a', 'obj a')
a.myName() // "a"
a.myLabel() // "obj a"

WARNING:下面这两种方式是常见的错误左房,实际上他们都存在一些问题:

// 和你想要的机制不一样。该方式并不会创建一个关联到Bar.prototype的新对象,他只是让Bar.prototype 直接引用Foo.prototype对象。因此当执行类似Bar.prototype.myLable = xxx 的赋值语句时,是直接修改Foo.prototype对象本身。
Bar.prototype = Foo.prototype
// 基本上满足需求,但是可能会产生一些副作用。该方式的确会创建一个关联到Foo.prototype的新对象。但是他使用的Foo(...)的“构造函数调用”,如果函数Foo有一些副作用(比如写日志、修改状态、注册到其他对象、给this添加数据属性等),就会影响到Bar()的“后代”。
Bar.prototype = new Foo()

TIPS:ES6添加了辅助函数Object.setPrototypeOf(...),可以作为标准且可靠的方式来修改对象关联:

// ES6之前使用Bar.prototype = Object.create(Foo.prototype)
Bar.prototype = Object.create(Foo.prototype)
// ES6 开始可以直接使用修改现有的Bar.prototype
Object.setPrototypeOf(Bar.prototype, Foo.prototype)

整数的安全范围

能够被“安全”呈现的最大整数是2^53-1,即9007199254740991,在ES6中被定义为Number.MAX_SAFE_INTEGER。最小整数是-9007199254740991,在ES6中被定义为Number.MIN_SAFE_INTEGER。

toString

  • 基本类型值得字符串化规则

    1. null -> "null"
    2. undefined -> "undefined"
    3. true -> "true"
  • 普通对象除非自定义,否则toString() (Object.prototype.toString())返回内部属性[[class]]的值,如“[object Object]”
  • 如果对象有自己的toString方法,字符串化时会调用该方法并使用其返回值。
  • 数组的默认toString()方法经过了重新定义,将返回单元字符串化以后在用“,”链接起来
  • JSON字符串化:

    1. JSON.stringify(val, replacer, space),在将对象序列化为字符串时也用到了toString
    2. 不安全的JSON值:undefined,function,symbol和包含循环引用的对象,JSON.stringify(...)在对象中遇到undefined、function和symbol时会自动将其忽略,在数组中则会返回null(以保证单元位置不变)。
    3. 如果对象中定义了toJSON()方法,JSON字符串化时会首先调用该方法,然后用它的返回值来进行序列化。toJSON()应该“返回一个能够被字符串化的安全的JSON值”,而不是“返回一个JSON字符串”

toNumber

  • 非数字值转换数字逻辑:

    1. true -> 1
    2. false -> 0
    3. undefined -> NaN
    4. null -> 0

toBoolean

  • 以下这些值都是假值:

    1. undefined
    2. null
    3. false
    4. 0、+0、-0和NaN
    5. ""
      除以上假值列表的数据外均为真值

~ 运算符(即字位操作“非” ) 和 |(即字位操作“或” )

  • 字位运算符只适用于32位整数,运算符会强制操作数使用32位格式,这是通过抽象操作toInt322来实现的。
  • ~x 大致等同于 -(x+1), 如 ~42 = ~(42+1) ==> -43。在-(x+1)中唯一能得到0的x值是-1,也就是说如果x为 -1 时,~x会返回假值0,其他情况下则返回真值。根据该规则可以将~和indexOf一起将结果强制转换为真/假值:
if (~a.indexOf('xxx')) { // true
  // 找到匹配的!
}
  • ~~x: 截除数字值的小数部分
0

评论 (0)

取消