从this指向开始谈谈JavaScript函数的一些内部实现

###** arguments** 废话很少说,直接先看一段最简单的函数:数组

function getSum(a,b,c,d){
	return a+b+c+d
}

啥意思?教你求和?app

答案就是求和,你没看错。求4个5个数的和还能够这样写,要是10个呢?a+b+c+d+e.....函数

那要是100个呢?完了,字母都不够用了。其实你能够这样写:this

function getSum(){
	var sum = 0
	for(var i=0;i<arguments.length;i++){	
		sum += arguments[i]
	}
	return sum
}
console.log(getSum(1,2,3,4))  //输出10

函数内部的arguments是什么?也没有声明,也没有传参。其实它是函数的隐式参数,每一个函数都会有,而且arguments是一个伪数组,用来代替不肯定个数的参数的,能够用arguments加下标的方式获取传给getSum函数的参数,它还有length属性,这些都与真正的数组类似,但它没有数组的方法,好比push、jion等。prototype

this

除了arguments,函数还有个隐式参数,即this。有函数的地方就会有this,this指向的是函数调用的上下文,上下文?啥意思?记得初中高中作语文阅读题最怕的一句话就是:联系上下文理解某句话或某个词语的意思。code

可是若是咱们明白了函数的四种调用方式,对应this有四种指向,就不怕不清楚this的指向了。对象

1. 函数做为普通函数被调用;作用域

2. 函数做为对象的方法被调用;get

3. 函数做为构造器被调用;回调函数

4. 使用apply()和call()进行调用;

第一种:

var name = "cat"
function fun(){
	var name = "dog"
	console.log(this.name)    //cat
	console.log(this==window) //true
}
fun()

函数做为普通函数被调用,this指向全局做用域,即window对象,将这种调用方式与下面第二种会更加清楚。

第二种

var name = "cat"
var obj={
	name:"dog",
	getName:function(){
		console.log(this.name)
	}
}
obj.getName()  //dog

这种,函数 function ( ){ console.log(this.name) }做为objde方法getName被调用,因此this指向调用对象,即obj,因此this.name就是obj.name。

第三种

function Cat(name){
	this.name = name
}
var cat1 = new Cat("小黄")
console.log(cat1.name) //小黄

当Cat函数被运用关键字new调用时,就是被当作构造器调用的,这时会发生:

1.建立一个空对象{ }

2.让this指向这个空对象,那么这个空对象将被增长一个name属性,而且赋值为“小黄”,即{name:"小黄"}

3.最后让cat1指向这个对象,即cat1={name:"小黄"}

4.因此cat1.name值为“小黄”

这种方式this指向的是对象的实例。

第四种

每一个函数都有call()或apply()方法,能够运用他们来强制改变this的指向,语法为:函数名.call(指向对象,参数)。好比下面咱们能够限制让fun函数中的this指向对象obj或者fun本身。

var name = "cat"
function fun(){
	console.log(this.name)    
}
var obj={
	name:"pig"
}
fun()  //cat

改造后为

var name = "cat"
function fun(){
	console.log(this.name)    
}
var obj={
	name:"pig"
}
fun.call(obj)  //pig
fun.call(fun)  //fun 由于函数都有name属性,函数fun的name就是fun

再看下面的实际例子,或许会更加清楚:

document.getElementById("div").onclick=function(){
	console.log(this)  //div的DOM对象
	function fun(){
		console.log(this)  //window
	}
	fun()
}

上面这个例子其实就是第一种和第二种的结合,首先div被点击后,回调函数将被执行,其实在内部这个函数被赋值给了div对象的onclick属性,这就对应第二种,函数被当作对象的方法被调用,因此内部的this执行div对象。 可是其内部又还有一个内部函数fun,它被当作普通函数调用,对应第一种,因此this指向window,但咱们有时还想让这时的this指向div对象,能够有两种改造方式,that=this和call或者apply:

document.getElementById("div").onclick=function(){
	console.log(this)  //div的DOM对象
	var that = this
	function fun(){
		console.log(that)  //div的DOM对象
	}
	fun()
}
document.getElementById("div").onclick=function(){
	console.log(this)  //div的DOM对象

	function fun(){
		console.log(this)  //div的DOM对象
	}
	fun.call(this)
}

call和apply

call和apply的做用

1. 如上面所述,call和apply能够用来强制改变this指向

2. 用来借用其余对象的方法

在讲第2个做用以前先来看看call和apply的区别,它们的做用是同样的,惟一区别就在于参数的传递形式。

call传入的参数个数不固定,第一个参数是this的强制指向对象,第二个参数开始日后就是做为参数传给函数:

var fun=function(a,b,c){
	console.log([a,b,c])
}
fun.call(null,1,2,3)   // [1,2,3]

而apply的参数就是两个,第一个与call同样表明this的强制指向对象,第二个能够为数组,也能够为伪数组arguments:

var fun=function(a,b,c){
	console.log([a,b,c])
}
fun.apply(null,[1,2,3])   // [1,2,3]

为何咱们两次传的参数不一样,call传1,2,3, apply传[1,2,3],却输出相同的结果,这是由于js函数内部的实现运用了arguments,当运用call时,即为正常状况,函数内部让第一个参数对应arguments[0],第二个参数对应arguments[1],以此类推.......,当运用apply时,函数内部直接让arguments等于数组[1,2,3]。

Math.max()方法用来获取几个数中的最大值,如Math.max(1,2,3)==3,可是不能用于获取数组中的最大值,因此能够利用apply来将这个方法运用于数组:

function getMax(array){
	return Math.max.apply(null,array)
}
getMax([1,2,3]) // 3

如今再来看看call和apply的第二个做用:借用别人的方法。 咱们想让一个空对象{ }也有数组的push方法,实现以下:

function fnn(){
	var obj={}
	Array.prototype.push.apply(obj,arguments)
	console.log(obj)

}
fnn(1,2,3)

apply和call能够借用别人的方法的秘密就在于:函数内部运用了大量的this和arguments。正常状况下push的用法为

var arr=[ ]
arr.push(1);

push对应的函数,做为方法被空数组arr调用(第二种调用),因此其内部的this指向arr,可是call和apply却能够改变this指向,使得this指向obj,那么此后,arguments[0]将被赋值给obj[0],而不是arr[0],而arguments[1]将被赋值给obj[1],而不是arr[1],以此类推..........以此达到借用的目的。

相关文章
相关标签/搜索