每一个函数(普通函数、构造函数、内置类)都是FUnction这个内置类的实例,所以:函数.__proto__ === FUnction.prototype.所有函数都可以直接调取FUnction原型上的方法
Function.prototype => function anonymous(){} //是一个空的匿名函数
Function.prototype上最重要的几个方法: call / apply /bind
1. call
语法:函数.call([content], [params])
函数基于原型链找到Function.prototype.call这个方法,并且把它执行,在call方法执行的时候完成了一些功能
1. 让当前函数执行
2. 把函数中的this指向改为第一个传递给call的参数
3. 把传递给call的其余参数(params)当作参数信息传递给当前函数
* 如果执行call一个实参都没有传递,非严格模式下是让函数中的this指向window,严格模式下指向undefined
window.name = 'WINDOW'
let obj = {name: 'OBJ'}
function fn(){
console.log(this.name)
}
fn() //=>this: window, 严格下是undefined
fn.call(obj) //this: obj
fn.call() //=>this: window, 严格下是undefined
fn.call(null) //=>this: window, 严格下是null(第一个参数传递的是null/undefined/不传,非严格模式下this实现window,严格模式下传递的是谁this就是谁,不传就是undefined)
手写Function.prototype的call方法
~ function () {
function call(context, ...params) {
context = context || window
//调用sum.call(obj, 10, 20)时,this = sum
//context = obj, params = [10, 20]
context.$fn = this;
let rs = context.$fn(...params); //相当于调用了this,即sum方法,但是this的指向变成了context即obj
delete context.$fn //删除context没用的属性
return rs //返回sum函数调用后的结果
}
Function.prototype.call = call
}()
function sum(n, m) {
console.log(this)
return n + m
}
let obj = {
name: 'demo'
}
sum.call(obj, 10, 20)
一个call是让左边函数执行(this是传递的参数); 多个call是让最后传参的函数执行(this是window/undefined)
function fn1() {
console.log(1)
}
function fn2() {
console.log(2)
}
fn1.call(fn2) //1 context = fn2, this = fn1 => fn2.$fn = fn1, fn2.$fn() => 执行fn1, this = fn2
fn1.call.call(fn2) //2 这里第一步执行的时候,context = fn2, this = fn1.call => fn2.$fn = fn1.call, fn2.$fn() => 执行fn1.call, this = fn2.
//第二步: fn1.call() => context = window, this = fn2(由第一步得到) => window.$fn = fn2, window.$fn() => 执行fn2, this = window
Function.prototype.call(fn1) //没有输出 context = fn1, this = Function.prototype => fn1.$fn = Function.prototype, fn1.$fn() => 执行Function.prototype, this = fn1
Function.prototype.call.call(fn1) //1 第一步: context = fn1, Function.prototype.call => fn1.$fn = Function.prototype.call, fn1.$fn() => 执行Function.prototype.call,this = fn1
//第二步Function.prototype.call() : context = window, this = fn1(由第一步得到) => window.$fn = fn1, window.$fn() => 执行fn1, this = window
2.apply
和call方法一样,都是把函数执行,并且改变里面的this关键字。唯一的区别就是传递给函数参数的方式不同
- call是一个个传参
- apply是数组传参
let obj = {name: 'OBJ'}
function fn(n, m){
console.log(this.name)
}
fn.call(obj, 10, 20)
fn.apply(obj, [10, 20])
3.bind
和call / apply 一样,也是用来改变函数中this关键字的,只不过基于bind改变this,当前方法没没有被执行,类似于预先改变this
let obj = {name: 'OBJ'}
function fn(){
console.log(this.name)
}
document.body.onclick = fn //当事件出发fn中的this: body
//要求,点击body,让fn中的this指向obj
document.body.onclick = function(){ //this: body
fn.call(obj)
}
document.body.onclick = fn.bind(obj) //bind: 通过bind方法只是预先把fn中的this修改为obj,此时fn并没有执行,当点击事件才会执行fn。(call/apply都是在改变this的同时立即把方法执行了)
//在IE6~8中不支持bind。预先做啥事情的思想被称为"柯理化函数"