JavaScript提高篇

74 阅读1分钟

1.闭包原理

闭包是指我们想要返回一个函数,并且返回的函数内部引用着函数外部的变量,保存函数外的值。这个变量就相当于整个函数的一个背包。

function foo(){
  let counter = 0;
  let inner = function(){
    counter +=1
    return counter;
  }
  return inner
}
let f1 = foo()
console.log(f1())
//1
console.log(f1())
//2

面试考题

var data = [];
for (var i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}

data[0]();
//3
data[1]();
//3
data[2]();
//3

本质上data数组里面存着都是一个函数,整个函数体就是console.log(i),而执行data中的函数时寻找i时,函数体内没有i,只能找到全局的i,当然是3了。

修改成闭包形式

var data = [];
for (var i = 0; i < 3; i++) {
  data[i] = (function (j) {
    return function(){
      console.log(j)
    }
  })(i);
}

data[0]();
//1
data[1]();
//2
data[2]();
//3

2.手写call。

使用 call() 方法,您可以编写能够在不同对象上使用的方法,方法重用。

实现效果

let obj = {
  value: 'foo'
}
let value = 'outValue'
function foo(){
  console.log(this.value)
}
foo.call(obj)
//foo

思路:

将目标函数当成这个对象的一个属性,调用完成将其删除。

版本1(无参数)

Function.prototype.call1 = function(obj){
  obj.fn = this;
  obj.fn()
  delete obj.fn
}
foo.call2(obj)

版本2(有参数)

Function.prototype.call2 = function(obj){
  let obj = obj || window //传递为null时指向window
  obj.fn = this;
  let arg = []
  for(let i = 1;i<arguments;i++){
    arg.push(`arguments[${i}]`) 
  }
  let result = eqal(obj.fn(arg.join(','))) //别忘了返回值
  delete result
}

3.手写apply。

与call类似,只是传递参数的时候传递的是数组。

Function.prototype.apply1 = function (obj, arr) {
  obj = obj || window
  obj.fn = this
  let result
  if (!arr) {
    result = obj.fn()
  } else {
    let arg = []
    for (let i = 0; i < arguments.length; i++) {
      arg.push('arr[' + i + ']');
    }
    result = eval(`obj.fn(${arg.join(',')})`)
  }
  return result
}

4.手写bind。

版本1

Function.prototype.bind1 = function (obj) {
  self = this
  return function () {
    return self.apply(obj)
  }
}
foo.bind1(obj)()

版本2(可以传入参数,并且参数可以不定)

Function.prototype.bind2 = function (obj) {
  self = this
  //第一次传入参数
  let arg1 = [].slice.call(arrguments,1)
  return function () {
  //第二次传入参数
    let arg2 = [].slice.call(arrguments)
    let arg = arg1.concat(arg2)
    return self.apply(obj,arg)
  }
}

版本3(bind绑定后可以作为构造函数使用,可以使用new)

  self = this
  //第一次传入参数
  let arg1 = [].slice.call(arrguments,1)
  return function () {
  //第二次传入参数
    let arg2 = [].slice.call(arrguments)
    let arg = arg1.concat(arg2)
    return self.apply(this instance that ? this:obj,arg)
  }