Skip to content

引用值作为属性名情况

先 toString 再 valueOf,使用独一无二的 key 使用 symbol

js
var a = {}
var b = {}
b[a] = 123
b[a] === a["[object object]"] // true

es6 中可以使用 Map 或者 weakMap

constructor 误区

constructor 是继承的构造函数的 prototype.constructor 属性。
对象的__proto__属性指向函数的 prototype 属性。

js
function Person(name) {
  this.name = name
}

Person.prototype.sayName = function() {
  console.log(this.name)
}

// 原型链继承
function Woman(sex) {
  this.sex = sex
}
Woman.prototype = new Person("yan")

let woman1 = new Woman("female")
woman1.sayName() // yan

/* --------------------------------- */
// 构造函数
function Man(name, sex) {
  Person.call(this, name)
  this.sex = sex
}
let man1 = new Man("xun", "male")
man1.sayName() // error

// 组合继承
function Man(name, sex) {
  Person.call(this, name)
  this.sex = sex
}
Man.prototype = new Person() // 问题 Man.prototype.contructor === Person    man1.contructor !== Man
Man.prototype.contructor = Man // 修正 contructor, 修复instanceOf

let man1 = new Man("xun", "fale")
man1.sayName() // xun

// 原型式继承
function extend1(o) {
  // 等价于 Object.create(o)
  let Fn = function() {}
  Fn.prototype = o
  return new Fn()
}

//

let const 和 var 绑定变量的区别

区别是 let const 不会绑定在 window 上面。 var 会。 还有严格模式下函数中的 this 模式不会指向 window

js
let a = 1
console.log(window.a) // undefined
var b = 2
console.log(window.b) // 2

this 永远指向最后调用它的那个对象

a.b.c() c 中 this 指向的是 b,除了显示调用 this,其余都是

js
function foo() {
  console.log(this.a)
  return function() {
    console.log(this.a)
  }
}
var obj = { a: 1 }
var a = 2

foo.call(obj)()
// 1
// 2   // 还是指向的window

函数作为参数可以这样写

js
function test(fn) {
  console.log(typeof fn)
}

test(
  function() {
    console.log(this.a)
  }.call({ a: 1 })
)

// 输出
// 1
// undefined

setTimeout 使用 undefined 作为参数的情况

什么也不会发生

箭头函数 this 问题

里面的 this 是由外层作用域来决定的,且指向函数定义时的 this 而非执行时。 ps 这句话是错误的, 应该从函数调用栈的观点来看待

箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined

特殊例子

js
var obj = {
  name: "obj",
  foo1: () => {
    console.log(this.name)
  },
  foo2: function() {
    console.log(this.name)
    return () => {
      console.log(this.name)
    }
  }
}
var name = "window"

obj.foo2.call({ name: 3 })()

// 3
// 3
// 所以箭头函数的this 应该是执行时向父作用域的this看齐。 执行代码是应该是加上父作用域的执行情况

箭头函数的 this 不能通过 bind,apply,call 来更改,因为箭头函数自身没有 this,依赖的是父作用域中的 this,所以只能通过修改父作用域来修改 this。

更加的迷惑的行为

js
var name = "window"
var obj1 = {
  name: "obj1",
  foo1: function() {
    console.log(this.name)
    return () => {
      console.log(this.name)
    }
  },
  foo2: () => {
    console.log(this.name)
    return function() {
      console.log(this.name)
    }
  }
}
var obj2 = {
  name: "obj2"
}
obj1.foo1.call(obj2)() // obj2 obj2
obj1.foo1().call(obj2) // obj1 obj1  迷惑: 因为返回箭头函数,所以形成了闭包, 需要再次引用父作用域的this
obj1.foo2.call(obj2)() // window window
obj1.foo2().call(obj2) // window obj2

浏览器端 even loop 执行顺序

  1. 一开始整个脚本作为一个宏任务执行
  2. 执行中,同步代码直接执行,宏任务进入宏任务队列, 微任务进入本次宏任务的微任务队列
  3. 宏任务执行,微任务执行
  4. UI 线程 渲染进行
  5. web worker 进行
  6. 重复第二点

宏任务包括 script 、setTimeout、setInterval 、setImmediate 、I/O 、UI rendering

微任务包括 process.nextTick promise.then MutationObserver

微任务会被添加到本次宏任务的末尾, 而宏任务会到下一次执行

promise 误区

js
const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve("success") // 如果promise 没有 resolve   .then 不会生效。 打印结果是 1 2 4
  console.log(2)
})
promise.then(() => {
  console.log(3)
})
console.log(4)

// 1 2 4 3

一开始进入宏队列

js
console.log("1")
setTimeout(() => {
  // 放入下一个宏队列
  console.log("2")
})
Promise.resolve().then(() => {
  // 属于本次宏任务的微任务
  console.log("3")
})
console.log("4")

// 1 4 3 2

影响性能的可能因素

  1. === 性能优于 !== if 语句都需要化简为===
  2. while 循环性能优于 for 养成 while 循环的好习惯 while(len--)
  3. 不能使用++a, 性能比直接先++ 弱

charAt 和 直接字符串下标的区别

charAt 是 es3 方法, 下标是 es5+方法。 charAt 在越界时返回 ""。字符串下标会让人误以为是可写的。 实际不能写。

promise 再次理解

先上结论

  1. Promise 的状态一经改变就不能再改变。
  2. .then 和.catch 都会返回一个新的 Promise。catch 也会返回一个 resolve 状态的 Promise。如果 catch 没有抓到错, 也会返回一个新 promise,是调用 catch 的副本。同理上层有 reject 状态时,.then 也会一直返回一个 rejected 状态的副本
  3. catch 不管被连接到哪里,都能捕获上层的错误。(见 3.2)
  4. 在 Promise 中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如 return 2 会被包装为 return Promise.resolve(2)。
  5. Promise 的 .then 或者 .catch 可以被调用多次, 当如果 Promise 内部的状态一经改变,并且有了一个值,那么后续每次调用.then 或者.catch 的时候都会直接拿到该值。(见 3.5)
  6. .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获。(见 3.6)
  7. .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。(见 3.7)
  8. .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。(见 3.8)
  9. .then 方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为 catch 是.then 第二个参数的简便写法。(见 3.9)
  10. .finally 方法也是返回一个 Promise,他在 Promise 结束的时候,无论结果为 resolved 还是 rejected,都会执行里面的回调函数。
js
// 证明第二点 每次调用.then .catch 都是返回的新的promise,状态由上一个promise的状态决定 。除了.then 一个 rejected状态的promise返回rejected状态的promise,其余都返回resolved状态
var a = new promise(res => res(2))
console.log(a) // promise resolved:2
var b = a.then(console.log) // 2  promise resolved:2
console.log(b === a) //false
console.log(a) // 同上个a
b.then(console.log) // 2  promise resolved:2

证明 catch 返回的 promise

js
var a = Promise.reject(2) // reject: 2
var b = a.catch(console.log) // 2    resolve: undifined
var c = a.then(console.log)
console.log(c) // reject: 2
console.log(c === a) // false
console.log(b) // resolve: undifined

验证返回的 promise 副本是深拷贝还是浅拷贝。

下面例子证明是"浅拷贝"

js
var a = {}
var b = Promise.resolve(a);
var c = = b.catch().then(res=>console.log(res===a)) // true
console.log(b.catch() === b)  // false

promise .then .catch 参数非法的情况

js
Promise.resolve(1)
  .then(2) // 只生成副本
  .then(Promise.resolve(3)) // 只生成副本
  .then(console.log) // 1

await 误区和正确阅读

js
async function async1() {
  console.log("async1 start")
  await async2()
  console.log("async1 end")
}
async function async2() {
  console.log("async2")
}
async1()
console.log("start")
// 之前错误的观点 start => async1 start" => async2 => async1 end
// 实际运行结果 async1 start => async2 => start => async2

所以可以理解为 await 函数是当前函数.then 的一个语法糖。 然后 async 理解成一个 promise 的封装,所以 await 需要是 resolved 或者 rejected 状态的才行。

js
var a = new Promise(res => {
  console.log
})
// pending 状态的。

奇特的 Promise resolve

js
function a(fn) {
  setTimeout(() => {
    fn(123)
    console.log(1234)
  }, 3000)
}

var a = new Promise(res => {
  a(res)
})
a.then(console.log)
// 1234
// 123

其实也不奇特,函数是引用传递

vue 中碰到浏览器死循环的问题,可以换个浏览器试试。