JS -- Function.prototype上的几个重要方法

535 阅读2分钟

每一个函数(普通函数、构造函数、内置类)都是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。预先做啥事情的思想被称为"柯理化函数"