重要知识点:每节博客都会重复🙉🙉🙉
口诀🤨:四基两空一对象,还有五个falsy值。
七种数据类型
- number string bool symbol
- null undefined
- object
五个 falsy 值
- null undefined
- 0 NaN
- '' (空字符串)
你可能遇到过这样的 JS 面试题:
var obj = {
foo:function(){
console.log(this)
}
}
var bar = obj.foo
obj.foo() // 打印出的 this 是 obj
bar() // 打印出的 this 是 window
请解释最后两行函数的值为什么不一样?
函数调用
首先要从函数的调用开始讲起。
JS(ES5)里面有三种函数调用形式:
func(p1, p2)
obj.child.method(p1, p2)
func.call(context, p1, p2) // 先不讲 apply
一般,初学者都知道前两种形式,而且认为前两种形式「优于」第三种形式。
从看到这篇文章起,你一定要记住,第三种调用形式,才是正常调用形式:
func.call(context, p1, p2)
其他两种都是语法糖,可以等价地变为 call 形式:
func(p1, p2) 等价于
func.call(undefined, p1, p2)
obj.child.method(p1, p2) 等价于
obj.child.method.call(obj.child, p1, p2)
请记下来。(我们称此代码为「转换代码」,方便下文引用)
至此我们的函数调用只有一种形式:
func.call(context, p1, p2)
好了,我们开始讲this
规则一:如果不给任何的指向,this默认指向window
规则二:this是一个参数(个人意见)
假如没有this,不准用this
- 代码
let person = {
name: 'frank',
sayHi(){
console.log(`你好,我叫` + person.name)
}
}
- 分析 我们可以用直接保存了对象地址的变量获取 'name' 我们把这种办法简称为引用
问题一
- 代码
let sayHi = function(){
console.log(`你好,我叫` + person.name)
}
let person = {
name: 'frank',
'sayHi': sayHi
}
- 分析 person 如果改名,sayHi 函数就挂了 sayHi 函数甚至有可能在另一个文件里面 所以我们不希望 sayHi 函数里出现 person 引用
问题二
- 代码
class Person{
constructor(name){
this.name = name
// 这里的 this 是 new 强制指定的
}
sayHi(){
console.log(???)
}
}
- 分析 这里只有类,还没创建对象,故不可能获取对象的引用 那么如何拿到对象的 name ?
需要一种办法拿到对象
这样才能获取对象的 name 属性
我们看看 Python 是怎么做的
- 代码
class Person:
def __init__(self, name): # 构造函数
self.name = name
def sayHi(self):
print('Hi, I am ' + self.name)
person = Person('xiaoming')
person.sayHi()
- 特点
- 每个函数都接受一个额外的
self
- 这个
self
就是传进来的对象 - 只不过 Python 会偷偷帮你传对象
person.sayHi()
等价于person.sayHi(person)
person
就被传给self
了
JS 没有模仿 Python 的思路
走了一条更难理解的路,难到我不得不先给大家讲 Python 的思路
JS 在每个函数里加了 this
- 用 this 获取那个对象
let person = {
name: 'frank',
sayHi(this){
console.log(`你好,我叫` + this.name)
}
}
person.sayHi()
相当于person.sayHi(person)
然后 person 被传给 this 了(person 是个地址)
这样,每个函数都能用 this 获取一个未知对象的引用了
3. 方便 sayHi
获取 person
对应的对象。person.sayHi()
会隐式地把 person 作为 this 传给 sayHi
总结一下目前的知识
- 我们想让函数获取对象的引用
- 但是并不想通过变量名做到
- Python 通过额外的
self
参数做到 - JS 通过额外的
this
做到:person.sayHi()
会把 person 自动传给 sayHi, sayHi 可以通过 this 引用 person
其他
- 注意
person.sayHi
和person.sayHi()
的区别 - 注意
person.sayHi()
的断句(person.sayHi)( )
JS 怎么给出的两种调用
小白调用法
person.sayHi()
会自动把 person 传到函数里,作为 this
大师调用法
person.sayHi.call(person)
需要自己手动把 person 传到函数里,作为 this
应该学习哪种?
学习大师调用法,因为小白调用法你早就会了。
例一:
function add(x,y){
return x+y
}
1.没有用到 this
add.call(undefined, 1,2) // 3
2.为什么要多写一个 undefined
- 因为第一个参数要作为 this
- 但是代码里没有用 this
- 所以只能用 undefined 占位
- 其实用 null 也可以
例二:
Array.prototype.forEach2 = function(fn){
for(let i=0;i<this.length;i++){
fn(this[i], i, this)
}
}
1.this
是什么
由于大家使用 forEach2
的时候总是会用 arr.forEach2
,
所以 arr 就被自动传给 forEach2
了
2.this 一定是数组吗
- 不一定,比如
Array.prototype.forEach2.call({0:'a',1:'b'})
this 的两种使用方法
1.隐式传递
fn(1,2)
等价于fn.call(undefined, 1, 2)
obj.child.fn(1)
等价于obj.child.fn.call(obj.child, 1)
2.显示传递
fn.call(undefined, 1,2)
fn.apply(undefined, [1,2])
箭头函数
JS 为了让新人不会this也能正常使用就出了箭头函数,但是需要注意:
1.里面的 this 就是外面的 this
console.log(this) // window
let fn = () => console.log(this)
fn() // window
2.就算你加 call 都没有
fn.call({name:'frank'}) // window
有人说「箭头函数里面的 this 指向箭头函数外面的 this」,这很傻,因为箭头函数内外 this 就是同一个东西,并不存在什么指向不指向。
由于本人水平有限,如有描述不准确的地方请给我留言,欢迎交流~