1 typeof类型判断
typeof 是否能正确判断类型? instanceof 能正确判断对象的原理是什么 typeof 对于原始类型来说,除了 null 都可以显示正确的类型
typeof 1
typeof '1'
typeof undefined
typeof true
typeof Symbol ( )
typeof 对于对象来说,除了函数都会显示 object ,所以说 typeof 并不能准确判断变量到底是什么类型
typeof [ ]
typeof { }
typeof console. log
如果我们想判断⼀个对象的正确类型,这时候可以考虑使⽤ instanceof ,因为内部机制是通过原型链来判断的
const Person = function ( ) { }
const p1 = new Person ( )
p1 instanceof Person
var str = 'hello world'
str instanceof String
var str1 = new String ( 'hello world' )
str1 instanceof String
对于原始类型来说,你想直接通过 instanceof 来判断类型是不⾏的
2 类型转换
⾸先我们要知道,在 JS 中类型转换只有三种情况,分别是:
转Boolean
在条件判断时,除了 undefined , null , false , NaN , ‘’ ,0 , -0 ,其他所有值都转为 true ,包括所有对象
对象转原始类型
对象在转换类型的时候,会调⽤内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑⼀般来说如下 如果已经是原始类型了,那就不需要转换了 调⽤ x.valueOf() ,如果转换为基础类型,就返回转换的值 调⽤ x.toString() ,如果转换为基础类型,就返回转换的值 如果都没有返回原始类型,就会报错 当然你也可以重写 Symbol.toPrimitive ,该⽅法在转原始类型时调⽤优先级最⾼。
let a = {
valueOf ( ) {
return 0
} ,
toString ( ) {
return '1'
} ,
[ Symbol. toPrimitive] ( ) {
return 2
}
}
1 + a
四则运算符
它有以下⼏个特点: 运算中其中⼀⽅为字符串,那么就会把另⼀⽅也转换为字符串 如果⼀⽅不是字符串或者数字,那么会将它转换为数字或者字符串
1 + '1'
true + true
4 + [ 1 , 2 , 3 ]
对于第⼀⾏代码来说,触发特点⼀,所以将数字 1 转换为字符串,得到结果 ‘11’ 对于第⼆⾏代码来说,触发特点⼆,所以将 true 转为数字 1 对于第三⾏代码来说,触发特点⼆,所以将数组通过 toString 转为字符串 1,2,3 ,得到结果 41,2,3 另外对于加法还需要注意这个表达式 ‘a’ + + ‘b’
'a' + + 'b'
因为 + ‘b’ 等于 NaN ,所以结果为 “aNaN” ,你可能也会在⼀些代码中看到过 +‘1’ 的形式来快速获取 number 类型。 那么对于除了加法的运算符来说,只要其中⼀⽅是数字,那么另⼀⽅就会被转为数字
4 * '3'
4 * [ ]
4 * [ 1 , 2 ]
⽐较运算符
如果是对象,就通过 toPrimitive 转换对象 如果是字符串,就通过 unicode 字符索引来⽐较
let a = {
valueOf ( ) {
return 0
} ,
toString ( ) {
return '1'
}
}
a > - 1
在以上代码中,因为 a 是对象,所以会通过 valueOf 转换为原始类型再⽐较值。
3 This
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 ,那么你就错了,其实我们可以把上述代码转换成另⼀种形式
let fn2 = function fn1 ( ) {
return function ( ) {
return fn . apply ( )
} . apply ( a)
}
fn2 ( )
可以从上述代码中发现,不管我们给函数 bind ⼏次, fn 中的 this 永远由第⼀次 bind 决定,所以结果永远是 window
let a = { name : 'poetries' }
function foo ( ) {
console. log ( this . name)
}
foo . bind ( a) ( )
以上就是 this 的规则了,但是可能会发⽣多个规则同时出现的情况,这时候不同的规则之间会根据优先级最⾼的来决定 this 最终指向哪⾥。 ⾸先, new 的⽅式优先级最⾼,接下来是 bind 这些函数,然后是obj.foo() 这种调⽤⽅式,最后是 foo 这种调⽤⽅式,同时,箭头函数的this ⼀旦被绑定,就不会再被任何⽅式所改变。