JS的三座大山:原型链 、this、 AJAX
arguments和this
每个函数都有,除了箭头函数(新语法)
1.arguments
- 是包含所有参数的伪数组
没有数组共有属性(push、pop)的就是伪数组 - 代码
function fn(){
console.log(arguments)
}
fn()
- 如何传
arguments
1.调用fn即可传arguments
2.fn(1,2,3)
那么arguments
就是[1,2,3]的伪数组
因为给定参数后,发现是一个数组,但是没有数组的共有属性,因此是一个伪数组
Array.from()
:可以把任何不是数组的东西变成数组
2.this
- 代码
function fn(){
console.log(this)
}
fn()
- 如果不给任何的条件,那么
this
默认指向Window
(这个不常用,直接用Window
就好了)
- 如何传
this
目前可以用fn.call()
传this
- 如果传的
this
不是对象,那么JS会自动帮你封装成对象
传数字1,就会变成对象1
传undefined
,就会变成全局变量window
- 当传
fn.call(xxx,1,2,3)
时,xxx
会被自动转化为对象,而后面的1,2,3
就是arguments
call
的参数被分成两段
- 如何取消封装:传什么就想要什么
加一行代码:在声明中加'use strict'
这样传的是什么,this
就是什么
3.结论
this是隐藏参数,arguments是普通参数
this是一种参数
4.假设没有this
例1
let person = {
name: 'frank',
sayHi(){
console.log(`你好,我叫` + person.name)//此处直接用了person
}
}
4.1 分析
- 其中第四行:没有完成声明就直接用了对象
person
是合法的
因为:console.log
是一个函数,函数在此处还没有被调用,等调用的时候就person
已经存在了 - 因此:可以用直接保存了对象地址的变量获取
'name'
- 把这种方法简称为引用:一个变量保存了某个东西的地址
4.2 问题一
- 函数和变量名绑定在一起了,如果
person
改名,另一边没改,那么sayHi
函数就挂了 - 如果先声明函数,其中要用到这个变量还没有声明,那如何确定变量名是个问题
sayHi
函数甚至可能出现在另一个文件里面- 所以我们不希望
sayHi
函数里面出现person
的引用
4.3 问题二:对象还好,如果用类问题更大
class Person{ //声明一个person的类
constructor(name){
this.name = name
// 这里的 this 是 new 强制指定的
}
sayHi(){
console.log(???)
}
}
- 这里只有类,还没创建新对象 person,故不可能获取 person 的引用
5.需要一种办法拿到对象
这样才能获取对象的name属性
怎么在不知道一个对象的名字的情况下拿到一个对象的引用
5.1一种土方法,用参数
形参是可以代表一个东西的,如函数的x、y
可以代表将要传进来的实际参数1和2
- 对象
let person = {
name: 'frank',
sayHi(p){
console.log(`你好,我叫` + p.name)
}
}
person.sayHi(person)//person对象传给了函数
- 类
class Person{
constructor(name){ this.name = name }
sayHi(p){
console.log(`你好,我叫` + p.name)
}
}
person.sayHi(person)
- 在每一个函数前面的第一个参数上,加了一个参数,并且约定这个参数就是到时候的新对象
- Python用的就是这种方法,两步
1.sayHi
函数接受一个默认的参数self
2.当你调用sayHi
的时候,会故意复制一份person
放到括号内,这样self.name
就是person.name
于是:person.sayHi()
等价于person.sayHi(person)
(Python偷偷帮你传对象)
6.JS如何解决这个问题
6.1 JS没有在sayHi那里加一个参数,而是直接加了一个关键字,也就是this
- JS在每个函数里加了
this
- 在任何函数里面可以用this去获取那个目前你还不知道名字的对象
let person = {
name: 'frank',
sayHi(){ //这里相当于省掉了一个this,等价于 sayHi(this)
console.log(`你好,我叫` + this.name)
}
}
person.sayHi()
person.sayHi()
等价于person.sayHi(person)
- 第三行的
sayHi()
括号中有一个隐藏的this
,相当于sayHi(this)
- 然后
person
就被传给了this
(person
是个地址) - 这样,每个函数都能用
this
获取一个未知对象的引用了
总结
- 我们想让函数获取对象的引用(对象名不知道,但我们知道它肯定存在)
- 但是并不想通过变量名做到
- Python通过额外的self参数做到
- JS通过额外的this做到
person.sayHi()
会把person
自动传给sayHi
(传地址),sayHi
可以通过this
引用person
- 其他
注意person.sayHi
和person.sayHi()
的区别
注意person.sayHi()
的断句:(person.sayHi)()
7.引出另一个问题
person.sayHi
(对)person.sayHi(person)
(错)- 为什么省略的格式是对的,完整的形式反而是错的
7.1解决问题:两种调用
- 小白调用法
person.sayHi
会自动把person
传到函数里,作为this
- 大师调用法
person.sayHi.call(person)
需要手动把person
传到函数里,作为this
其中call是传递this的方法,传什么,this就是什么
8.大师调用法
JS出了一个新的调用方法call
,call
会使得所有东西明朗起来
-
person.sayHi.call(person)
-
call
后面传什么,this
就是什么
person.sayHi.call({name:'jack'})
传递一个对象,那么this
就是一个对象,this.name
就是jack
-
相当于:把一个函数放到对象上面,但这个函数和这个对象没有任何关系,只是恰好出现在一起而已
-
好处:使用
call
调用的好处是,多了很多细节(我们推荐使用这个!)
例2、不用this
- 调用
call
时,即使用不到this
,也要找个东西占位(一般用undefined
),否则会默认参数第一个为this
例3、用this(数组的隐藏函数中forEach
就含有this
)
这个函数的作用:遍历当前数组
this
就是当前数组,待传递
Array.prototype.forEach2 = function(fn){//是forEach的源代码
for(let i=0; i<this.length; i++){
fn(this[i],i )
}
}
array.forEach2.call(array,(item)=>console.log(item))
意思是forEach2
等于一个函数(这个函数接收另一个函数fn
),然后遍历this
,对于this
里面的每一项,调用fn
,同时传递一个i
this
一定要是数组吗?
不一定,还可以是自己写的伪数组,,因此this
就是可以任意指定的参数而已
Array.prototype.forEach2.call({0:'a',1:'b',length:2})
9.this传递的两种方法
- 隐式传递(相当于不管调用了什么函数,都传递了
this
)
fn(1,2)
等价于fn.call(undefined, 1, 2)
obj.child.fn(1)
等价于obj.child.fn.call(obj.child, 1)
(对象的属性的里面的函数,就是把fn全面的整个部分作为this
) - 显示传递
fn.call(undefined,1,2)
fn.apply(undefined,[1,2])
只是后面的参数要传数组而已
10.绑定this
- 使用
.bind
可以让this
不被改变
f2绑定了this的值,传的时候就只需要传递 p1 和 p2 的值
function f1(p1, p2){
console.log(this, p1, p2)
}
let f2 = f1.bind({name:'frank'})
// 那么 f2 就是 f1 绑定了 this 之后的新函数
f2(1,2) // 等价于 f1.call({name:'frank'})
.bind
还可以绑定其他参数
就是 f3 已经绑定了 this 和 p1 的值是什么,传递的时候只需要传 p2
let f3 = f1.bind({name:'frank'}, 'hi')
f3(1) // 等价于 f1.call({name:'frank'}, hi)