Appearance
引用值作为属性名情况
先 toString 再 valueOf,使用独一无二的 key 使用 symbol
js
var a = {}
var b = {}
b[a] = 123
b[a] === a["[object object]"] // truees6 中可以使用 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) // 2this 永远指向最后调用它的那个对象
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
// undefinedsetTimeout 使用 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 执行顺序
- 一开始整个脚本作为一个宏任务执行
- 执行中,同步代码直接执行,宏任务进入宏任务队列, 微任务进入本次宏任务的微任务队列
- 宏任务执行,微任务执行
- UI 线程 渲染进行
- web worker 进行
- 重复第二点
宏任务包括 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影响性能的可能因素
- === 性能优于 !== if 语句都需要化简为===
- while 循环性能优于 for 养成 while 循环的好习惯
while(len--) - 不能使用
++a, 性能比直接先++ 弱
charAt 和 直接字符串下标的区别
charAt 是 es3 方法, 下标是 es5+方法。 charAt 在越界时返回 ""。字符串下标会让人误以为是可写的。 实际不能写。
promise 再次理解
先上结论
- Promise 的状态一经改变就不能再改变。
- .then 和.catch 都会返回一个新的 Promise。catch 也会返回一个 resolve 状态的 Promise。如果 catch 没有抓到错, 也会返回一个新 promise,是调用 catch 的副本。同理上层有 reject 状态时,.then 也会一直返回一个 rejected 状态的副本
- catch 不管被连接到哪里,都能捕获上层的错误。(见 3.2)
- 在 Promise 中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如 return 2 会被包装为 return Promise.resolve(2)。
- Promise 的 .then 或者 .catch 可以被调用多次, 当如果 Promise 内部的状态一经改变,并且有了一个值,那么后续每次调用.then 或者.catch 的时候都会直接拿到该值。(见 3.5)
- .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获。(见 3.6)
- .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。(见 3.7)
- .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。(见 3.8)
- .then 方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为 catch 是.then 第二个参数的简便写法。(见 3.9)
- .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) // falsepromise .then .catch 参数非法的情况
js
Promise.resolve(1)
.then(2) // 只生成副本
.then(Promise.resolve(3)) // 只生成副本
.then(console.log) // 1await 误区和正确阅读
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其实也不奇特,函数是引用传递
