原始值:boolean、null、undefined、number、string、symbol
在条件判断时,除了 undefined
, null
, false
, NaN
, ‘‘
, 0
, -0
,其他所有值都转为 true
,包括所有对象。
对象在转换类型的时候,会调用内置的 [[ToPrimitive]]
函数,对于该函数来说,算法逻辑一般来说如下:
x.toString()
,转换为基础类型的话就返回转换的值。不是字符串类型的话就先调用 valueOf
,结果不是基础类型的话再调用 toString
x.valueOf()
,如果转换为基础类型,就返回转换的值当然你也可以重写 Symbol.toPrimitive
,该方法在转原始类型时调用优先级最高。
let a = {
valueOf() {
return 0
},
toString() {
return ‘1‘
},
[Symbol.toPrimitive]() {
return 2
}
}
1 + a // => 3
加法运算符不同于其他几个运算符,它有以下几个特点:
1 + ‘1‘ // ‘11‘
true + true // 2
4 + [1,2,3] // "41,2,3"
如果你对于答案有疑问的话,请看解析:
1
转换为字符串,得到结果 ‘11‘
true
转为数字 1
toString
转为字符串 1,2,3
,得到结果 41,2,3
另外对于加法还需要注意这个表达式 ‘a‘ + + ‘b‘
‘a‘ + + ‘b‘ // -> "aNaN"
因为 + ‘b‘
等于 NaN
,所以结果为 "aNaN"
,你可能也会在一些代码中看到过 + ‘1‘
的形式来快速获取 number
类型。
那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字
4 * ‘3‘ // 12
4 * [] // 0
4 * [1, 2] // NaN
我们先来看几个函数调用的场景
function foo() {
console.log(this.a)
}
var a = 1
foo()
const obj = {
a: 2,
foo: foo
}
obj.foo()
const c = new foo()
接下来我们一个个分析上面几个场景
foo
来说,不管 foo
函数被放在了什么地方,this
一定是 window
obj.foo()
来说,我们只需要记住,谁调用了函数,谁就是 this
,所以在这个场景下 foo
函数中的 this
就是 obj
对象new
的方式来说,this
被永远绑定在了 c
上面,不会被任何方式改变 this
说完了以上几种情况,其实很多代码中的 this
应该就没什么问题了,下面让我们看看箭头函数中的 this
function a() {
return () => {
return () => {
console.log(this)
}
}
}
console.log(a()()())
首先箭头函数其实是没有 this
的,箭头函数中的 this
只取决包裹箭头函数的第一个普通函数的 this
。在这个例子中,因为包裹箭头函数的第一个普通函数是 a
,所以此时的 this
是 window
。另外对箭头函数使用 bind
这类函数是无效的。
最后种情况也就是 bind
这些改变上下文的 API 了,对于这些函数来说,this
取决于第一个参数,如果第一个参数为空,那么就是 window
。
那么说到 bind
,不知道大家是否考虑过,如果对一个函数进行多次 bind
,那么上下文会是什么呢?
let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)() // => ?
如果你认为输出结果是 a
,那么你就错了,其实我们可以把上述代码转换成另一种形式
// fn.bind().bind(a) 等于
let fn2 = function fn1() {
return function() {
return fn.apply()
}.apply(a)
}
fn2()
可以从上述代码中发现,不管我们给函数 bind
几次,fn
中的 this
永远由第一次 bind
决定,所以结果永远是 window
。
let a = { name: ‘yck‘ }
function foo() {
console.log(this.name)
}
foo.bind(a)() // => ‘yck‘
以上就是 this
的规则了,但是可能会发生多个规则同时出现的情况,这时候不同的规则之间会根据优先级最高的来决定 this
最终指向哪里。
首先,new
的方式优先级最高,接下来是 bind
这些函数,然后是 obj.foo()
这种调用方式,最后是 foo
这种调用方式,同时,箭头函数的 this
一旦被绑定,就不会再被任何方式所改变。
如果你还是觉得有点绕,那么就看以下的这张流程图吧,图中的流程只针对于单个规则。

1、call() 、apply()可以看作是某个对象的方法,通过调用方法的形式来间接调用函数,让函数在某个指定的对象下执行。bind() 就是将某个函数绑定到某个对象上
2、bind() 方法会返回执行上下文被改变的函数而不会立即执行,而前两者是直接执行该函数
3、bind虽然只返回一个函数并不调用,但是仍然可以传参,使用bind()方法使函数拥有预设的初始参数,这些参数会排在最前面,传给绑定函数的参数会跟在它们后面
1 function list(){ 2 // 让类数组arguments拥有数组的方法slice,这个函数实现了简单把类数组转换成数组 3 return Array.prototype.slice.call(arguments); 4 } 5 6 list(1,2,3);//[1,2,3] 7 8 //给list绑定一个预设参数4 9 var list1 = list.bind(undefined,4); 10 11 list1();//[4] 12 13 list1(1,2,3);//[4,1,2,3]
原文:https://www.cnblogs.com/wanwanli/p/10855962.html