arguments对象
定义
js允许函数有不定数目的实参,当函数调用时,有时候需要知道传进来多少个实参,所以需要一种机制可以获取到传递的实参信息,这就是arguments的由来。
使用arguments的length属性,可以获取调用时传入实参的个数。
与数组的关系
在函数中,arguments是所有参数的集合,是一个伪数组。
之所以是伪数组是因为arguments没有数组的prototype
可以看到arguments的__proto__没有指向数组的prototype,而是直接到了Object这个根原型。
如果想让arguments使用数组的方法,那么可以使用Array.from(arguments)把伪书组变成真正的数组
this
arguments属于js函数中的普通参数,而this是隐式参数。
this在函数中指向的是运行时的环境。
例如下面代码:
var fo={
myname:'yan',
fn:function(){
console.log(this.myname)}
}
myname='xi';
var foo=fo.fn;
foo() //'xi'
fo.fn() //'yan'
造成两者的不同是因为运行环境的改变,在js中,当处于顶层的运行环境,this指向的是window。
而fo.fn()函数运行在fo对象环境下,所以造成两者结果不同。
那么,为什么obj.foo()就是在obj环境执行,而一旦var foo = obj.foo,foo()就变成在全局环境执行?
函数在对象中的内存图
假设现在有两个对象var obj={age:18}和var obj2={fn:function(){}}
它的内存图结构是这样的
在stack中,分别保存了对象的地址,分别指向heap中的数据。
在obj2中,虽然这个变量在stack中保存的是地址,但是在heap中实际的数据fn对应的值保存的是一个函数的地址,因为函数属于复杂数据结构,会另外开辟空间保存。
因为函数是一个单独的值,所以可以在不同环境下运行
var f =function(){}
f() //在全局环境下运行
var obj={f:f}
obj.f() //在obj环境下运行
运行环境的影响
运行环境会影响函数内部的变量
var f = function () {
console.log(x);
};
上面代码中,函数体里面使用了变量x。该变量由运行环境提供。
那么,因为函数可以在不同的运行环境下运行,所以我们需要一种机制,在内部获取当前的运行环境。
现在我有一个需求
var person={
name:qiuyanxi,
say:function(){
console.log(?)}
}
怎样才能调用say的函数打印出person的name呢?
我可以写console.log(person.name)??
这样会造成一个问题,如果我修改了person这个对象名,所以也要同步修改函数中的person,一个函数还好,如果有非常多的函数,需要打印出person.age、person.mum呢?
再问一个问题:
假设某个对象没有被创建出来呢?我要如何打印未来要创建出来的这个对象的name?
function Person(name){
this.name=name //这里的this是强制的关键字,代表实例对象自己
}
Person.prototype.fn=function(){
console.log(xxx?.name)
}
解决方法
解决方法是有的,就是用参数的方式传自己
var person={
name:qiuyanxi,
say:function(p){
console.log(p.name)}
}
person.say(person) //qiuyanxi
function Person(name){
this.name=name //这里的this是强制的关键字,代表实例对象自己
}
Person.prototype.fn=function(p){
console.log(p.name)
}
var obj=new Person('ldh');
obj.fn(obj) //ldh
这种方式确实很傻,但是很容易让人理解。python用了这种方法,只是在调用时,把括号里的自己给隐藏起来了
python是把对象自己用了self关键字,代表着实例对象自己。
但js用了this,这个this在定义时候被隐藏了
class Person{
constructor(name){
this.name=name
}
say(//这里的this被隐藏了){
console.log(this.name)}
}
var pers=new Person('ldh');
pers.say()
pers.say() 实际上就是把pers当成this传给say(),但是将过程都隐藏起来了。
- this隐式地被当成是参数,不能写出来,写出来报错。在这点的理解上真的python好理解多了。【python是写了形参self,隐藏了实参自己,而js是将形参this跟实参自己都隐藏起来了】
需要注意一点,当你用了this关键字之后,pers.say(pers)这种写法虽然不报错,但并不是正确的。因为per会被当成是arguments而不是this。所以还是老老实实写pers.say()吧。
结论
通过上述,我们已经知道,this能够帮助在一个对象中找到当前对象(this)的属性或方法。
我们或许可以用一个结论来概述:this就是其直属的函数所属的对象
先来两个条件:
1、如果一个函数没有标明有直属对象,那么直属对象就是window。
2、let、const之后的变量不在window属性下,不属于global领域
来看一个例子:
let name='qiu';
let a={
name:'da',
say(){console.log(this.name)}
}
let b=a.say
b() // ""
a.say() //"da"
上面代码中,由于a.say函数拷贝了地址给b,此时函数的运行环境已经发生了变化:
this的直属函数变成了b
b函数不属于window下的属性,此时没有直属对象。那么this就会找到window,window就会找它下面有没有name。代码第一行,由于let后的name属性不属于window,所以window就找到自己的name属性,最后输出为空。
总而言之,如果this所属的函数没有直属对象,那么就会找window。如果有,那么就找这个直属对象。
但是,上面都是基于普通函数而得出的结论,如果换成箭头函数则不行。
箭头函数中的this
来看几个隐晦的例子
let people = {
name: '若愚',
sayName() {
setTimeout(function(){
console.log(this.name)
}, 0)
}
}
window.name='123'
people.sayName() // '123'
上面代码中,this属于setTimeout,而setTimeout所属的对象是window,所以打印出来的是window.name。
如果把函数变成箭头函数呢?
箭头函数本身是没有this的,它会根据定义时的上下文往外捕获来捕获this。
let people = {
name: '若愚',
sayName:()=>{
console.log(this.name)
}
}
window.name='123'
people.sayName() //123
上面函数中,箭头函数挂在people.sayName中,但是此时箭头函数中的this会往外层查找,从而找到的是window,此时this变成了window。
再来看这个代码
let people = {
name: '若愚',
sayName() {
setTimeout(()=>{
console.log(this.name)
}, 0)
}
}
window.name='123'
people.sayName() //若愚
上面代码中,this的直属函数挂在window.setTimeout下,此时所属函数是window,但是由于是箭头函数,箭头函数本身是没有this的,里面的this会根据定义时的上下文往外层捕获,于是就找到sayName这个函数,捕获到的就是sayName函数所属的对象people。
最后
最终我们得出结论: 1、普通函数下,this就是直属函数的所属对象
2、箭头函数下本身是没有this的,如果写了this,那么它会往定义时的上下文寻找并捕获外层的this。