Open mileOfSunshine opened 5 years ago
加号操作符的两个操作数如果都是值类型
// Example1:
console.log(1 + 2); // 3
// Example2:
console.log( 1 + "3"); // "13"
console.log("3" + 1); // "31"
console.log(1 + 2 +"3"); // "33"
console.log("3" + "4"); // "34"
// Example3:
console.log(1 + null); // 1
console.log(1 + undefined ); // NaN
console.log(1 + NaN); // NaN
// Example4:
console.log("3" + null); // "3null"
console.log("3" + undefined ); // "3undefined"
console.log("3" + NaN); // "3NaN"
// Example5:
console.log(1 + {}); // "1[object Object]"
console.log(1 + []); // "1"
this
与函数本身以及上下文都没有关系,只取决于函数是如何被调用的
纯粹的函数调用, this
指向 window
对象方法调用,必须是a.b()
或a["b"]()
这种形式调用,this
指向对象
构造函数调用, new Foo()
时,会自动生成一个空对象并返回,this
指向这个新生成的空对象
call, apply, bind
, this
可以随意指定。
箭头函数没有自己的 this
对象。内部的 this
就是定义时上层作用域中的 this
。箭头函数内部的 this
指向是固定的,普通函数的 this
指向是可变的。
function Persion(name) {
this.name = name
const school = {}
school.primary = '实验小学'
school.show = () => {
console.log(this)
}
school.show() // school.show.call() 的语法糖
}
const pp = new Persion('Jane') // => Persion { name: "Jane" }
function outer() {
return function inner() {
const obj = {}
obj.show = () => {
console.log('this>', this)
}
return obj
}
}
outer()().show() // => Window
// bind 指定闭包函数 inner 中的 this 指向
outer().bind(outer)().show() // => outer
补充:
建议使用
new Date('2019/01/01')
// 或
new Date(2019, 0, 1)
fun.apply(thisArg, [argsArray])
fun.call(thisArg, arg1, arg2, ...)
apply 和 call 的功能是一样的,只是传入的参数列表形式不同. apply:最多只能有两个参数——新this对象和一个数组 argArray。 call:它可以接受多个参数,第一个参数与 apply 一样,后面则是一串参数列表。
thisArg
在 fun 函数运行时指定的 this 值
function fun() {
console.log(this)
}
fun.apply(this) // => Window
fun.apply(fun) // => fun () { console.log(this) }
fun.apply({}) // => {}
fun.apply() // => Window
手写:
Function.prototype.myCall = function (context) {
if (typeof this !== 'function') {
throw new Error('Type Error')
}
context = context || window
const fnSymbol = Symbol()
context[fnSymbol] = this
const args = [...arguments].slice(1)
const result = context[fnSymbol](...args)
delete context[fnSymbol]
return result
}
Function.prototype.myApply = function (context) {
if (typeof this !== 'function') {
throw new Error('Type Error')
}
context = context || window
const fnSymbol = Symbol()
context[fnSymbol] = this
const args = arguments[1]
const result = context[fnSymbol](args)
delete context[fnSymbol]
return result
}
Function.prototype.myBind = function(context) {
if (typeof this !== 'function') {
throw new Error('Type Error')
}
const args = [...arguments].slice(1)
const fn = this
return function Fn() {
return fn.apply(
this instanceof Fn ? this : context,
args.concat(...arguments)
)
}
}
如果将类中的方法提取出来单独使用,this
会指向该方法运行时所在的环境。
解决方法:
this
, react里方法绑定就是这种形式。
class Logger {
constructor() {
this.printName = this.printName.bind(this)
}
}
this
总是指向定义时所在的对象。
class Obj {
constructor() {
this.getThis = () => this
}
}
this
指向的是类,不是实例super
对象上调用constructor()
方法里面的 this
上面,也可以定义在类的最顶层。#
)#
this
引用,只要是在类的内部,实例也可以引用私有属性static
关键字,表示这是一个静态的私有属性或私有方法。 new.target
属性new.target
会返回子类super
super
既可以当作函数使用,也可以当作对象使用。
函数使用:
super
代表父类的构造函数,但返回的是子类的实例,即 super
内部的 this
指的是子类的实例,super
相当于 父类.prototype.constructor.call(this)
constructor
方法中调用 super
方法的原因: 子类自己的 this
对象必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用 super
方法, 子类就得不到 this
对象。 super
之后,才可以使用 this
关键字,否则会报错。这是因为子类实例的构建基于父类实例,只有 super
方法才能调用父类实例。对象使用:
父类.prototype
)super
调用super
调用的父类的方法时,方法内部的 this
指向当前的子类实例。super
调用的父类方法时,方法内部的 this
指向当前的子类,而不是子类的实例?.
)??
)length 是函数的一个属性,表示函数希望接收的命名参数的个数,即形参的个数。
(function () {}).length // 0
(function (a) {}).length // 1
(function (a, b) {}).length // 2
(function (a) {}).length // 1
/* 指定函数默认值会造成length属性失真 */
(function (a = 5) {}).length // 0
/* length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数 */
(function (a, b, c = 5) {}).length // 2
/* 如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了 */
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
/* rest 参数也不会计入length属性 */
(function(...args) {}).length // 0
? 类的 prototype 属性和proto属性
Class 作为构造函数的语法糖,同时有prototype
属性和__proto__
属性,因此同时存在两条继承链。
(1)子类的__proto__
属性,表示构造函数的继承,总是指向父类。
(2)子类prototype
属性的__proto__
属性,表示方法的继承,总是指向父类的prototype
属性。
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
? 实例的 __proto__ 属性
var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red');
p2.__proto__ === p1.__proto__ // false
p2.__proto__.__proto__ === p1.__proto__ // true
Array.from(arguments)
Array.from(new Set(['a', 'b'])) // ['a', 'b']
Array.of(3, 11, 8) // [3,11,8]
copyWithin(target, start = 0, end = this.length)
[1, 2, 3, 4, 5].copyWithin(0, 3)// [4, 5, 3, 4, 5]
[1, 4, -5, 10].find((n) => n < 0) // -5
[1, 5, 10, 15].findIndex((value, index, arr) => value > 9) // 2
new Array(3).fill(7) // [7, 7, 7]
[1, 2, [3, 4]].flat() // [1, 2, 3, 4]
[1, [2, [3]]].flat(Infinity) // [1, 2, 3]
arr.flatMap(function callback(currentValue[, index[, array]]) { // ... }[, thisArg])
// 当前数组成员、当前数组成员的位置(从零开始)、原数组[1, 2, 3, 4].flatMap(x => [[x * 2]]) // [[2], [4], [6], [8]]
const arr = [5, 12, 8, 130, 44]; arr.at(2) // 8
? 事件循环机制(Event Loop)
结论:
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
return Promise.resolve().then(()=>{
console.log('async2 end1')
})
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
new Promise(resolve => {
console.log('Promise2')
})
.then(function() {
console.log('promise2-1')
})
console.log('script end')
解析:
script start
async2 end
,async2 函数中遇到微任务 then(()=>{ console.log('async2 end1') }),产生第一个微任务,此时保留 async1() 上下文,然后跳出 async1 函数。执行其他代码。Promise
。因为 resolve,接着遇到第二个微任务 .then(function() { console.log('promise1') })Promise2
。因为没有resolve,then 无法生成一个微任务。script end
async2 end1
promise1
,该微任务遇到 then(function() { console.log('promise2') }),产生一个新的微任务,继续执行该微任务。promise2
,当前微任务执行完毕,执行权回到 async1,执行 await 会返回一个 promise,执行完成,执行await后面的语句,输出 async1 end
setTimeout
? 柯里化
可变参数版(采用剩余参数实现)
function curry(...args1) {
let args = [...args1]
const fn = function (...argv) {
return curry.apply(this, args.concat(argv))
}
fn.toString = function() {
return args.reduce((a, b) => {
return a * b
}, 1)
}
return fn
}
// 例子
console.log(curry(1)(2)(3)(4) + '') // => 24
// 函数转字符串:会先调用 toString(),返回非原始值或是无此方法,则将调用 valueOf()
// 函数转数组:会先调用valueOf(),返回非原始值或是无此方法,则将调用 toString()
function curry(fn, arity = fn.length, ...args) {
return arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args)
}
// 例子
var multi = curry((a, b, c) => a * b * c)
multi(1)(2)(3) // => 6
另一个种写法
function curry(fn) {
const self = this
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(self, args)
} else {
return function(...args1) {
return curried.apply(self, args.concat(...args1))
}
}
}
}
//function add(a, b, c) {
// return a + b + c
//}
function add(...args) {
return args.reduce((sum, cur) => {
return sum += cur
})
}
const curryAdd = curry(add)
curryAdd(1)(2)(3) // => 6
? 反柯里化
Function.prototype.uncurry() {
const self = this
return function() {
return Function.prototype.call.apply(self, arguments)
}
}
function uncurry(fn) {
return function() {
const that = [].shift.call(arguments, 0)
return fn.apply(that, arguments)
}
}
? 箭头函数 & 普通函数
? 数据类型
基本类型:String、Boolean、Number、Symbol、null、undefined
根据浮点数的定义,非整数的Number类型无法用 ==(===)来比较,浮点数运算的精度问题导致等式左右的结果并不是严格相等。
? if条件不成立的几个值
false, null, undefined, "", 0 NaN
? 为什么给对象添加的方法能用在基本类型上?
.
运算符提供了装箱操作,它会根据基础类型构造一个临时对象,使得我们能在基础类型上调用对应对象的方法。? 装箱 & 拆箱