闭包内存回收和this的四个绑定规则

闭包内存泄漏的案例:

function createFnArray () {
  // 占据的空间是4M
  var arr = new Array(1024 * 1024).fill(1)
  return function () {
    console.log(arr.length)
  }
}

var arrayFns = []
for (var i = 0; i < 100; i++) {
  arrayFns.push(createFnArray())
}

在内存的表现:

 销毁数据,避免内存泄漏的操作:

setTimeOut (() => {
    arrayFns = null
},2000)

  • AO对象不会被销毁时,是否里面的所有属性都不会被释放?
  1. 下面这段代码中name属于闭包的父作用域里面的变量
  2. 我们知道形成闭包之后count一定不会被销毁掉,那么name是否会被销毁掉呢?

代码:

function foo() {
  var name = 'why'
  var age = 18

  function bar() {
    debugger
    console.log(name)
  }
  return bar
}

var fn = foo()
fn()

 没有使用的age属性,js引擎会销毁这个age属性

为什么需要this?

有了this,我们编写代码更加方便

this在全局环境的指向:

在大多数情况下,this都是出现在函数中

在全局作用域下

浏览器:window(globalObject)

Node环境: {}

在函数中使用:

  • 所有的函数在被调用时,都会创建一个执行上下文
  • 这个上下文中记录着函数的调用栈,AO对象等
  • this也是其中的一条记录

this指向什么,跟函数所处的位置是没有关系的,跟函数被调用的方式是有关系

 默认绑定

独立函数调用

  • 独立的函数调用我们可以理解成函数没有被绑定到某一个对象上进行调用
  • 通过下面的案例看一下,常见的默认绑定:

// 默认绑定:独立函数调用
function foo () {
    console.log(this)
}

foo()

隐式绑定

调用方式是通过某个对象进行调用的

  • 也就是它的调用位置中,是通过某个对象发起的函数调用

  • ​​​​​​​

 显示绑定

function foo () {
    console.log("函数被调用了",this)
}

// foo直接调用和call/apply调用的不同在于this绑定的不同
// foo直接调用指向的是全局对象(window)
// foo()

var obj = {
    name: "obj"
}

// call/apply是可以指定this的绑定对象的
foo.call(obj)
foo.apply(obj)
foo.apply("aaa")

call和apply有什么区别

 call和apply在执行函数时,是可以明确的绑定this,这个绑定规则称之为显示绑定

function sum(num1,num2,num3) {
  console.log(num1 + num2 + num3,this)
}
sum.call('call',20, 30, 40)
sum.apply('apply',[20, 30, 40])

显示绑定-bind

new绑定

  • JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字
  • 使用new关键字来调用时,会执行如下的操作:
  1. 创建一个全新的对象
  2. 这个新对象会被执行prototype连接
  3. 这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成)
  4. 如果函数没有返回其他对象,表达式会返回这个新对象

 内置函数的绑定思考

 规则优先级

  • 默认规则的优先级最低
  1. 毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this
  • 显示绑定优先级高于隐式绑定
  • new绑定的优先级高于隐式绑定

new绑定 > 显式绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)

特殊绑定-忽略显式绑定

apply/call/bind: 当传入null、undefined时,自动将this绑定成全局对象

function foo() {
    console.log(this)
}

foo.apply('abc')
foo.apply({})

foo.apply(null)
foo.apply(undefined)

var bar = foo.bind(null)
bar()

 特殊绑定-间接函数引用

var obj1 = {
    name: "obj1",
    foo: function() {
        console.log(this)
    }
}

var obj2 = {
    name: "obj2"
};

(obj2.bar = obj1.foo)()

这里就是独立函数调用

箭头函数

箭头函数是ES6之后增加的一种编写函数的方法,并且它比函数表达式要更加简洁

  1. 箭头函数不会绑定this、arguments属性
  2. 箭头函数不能作为构造函数来使用(不i能和new一起来使用,会抛出错误)

// 1.编写箭头函数
// 1>(): 参数
// 2> =>:  箭头
// 3> {}: 函数的执行体
var foo = (num1, num2, num3) => {
  console.log(num1, num2, num3)
  var result = num1 + num2 + num3
  console.log(result)
}

function bar(num1, num2, num3) {

}

// 高阶函数在使用时,也可以传入箭头函数
var nums = [10, 20, 45, 78]
nums.forEach((item, index, arr) => {})

// 箭头函数有一些常见的简写:
// 简写一:如果参数只有一个,()可以省略
nums.forEach(item => {
  console.log(item)
})

// 简写二:如何函数执行体只有一行代码,那么{}也可以省略
// 强调:并且它会默认将这行代码的执行结构作为返回值
nums.forEach(item => console.log(item))
var newNums = nums.filter(item => item % 2 === 0)
console.log(newNums)

// filter/map/reduce
var result = nums.filter(item => item % 2 === 0)
                 .map(item => item * 100)
                 .reduce((preValue, item) => preValue + item)
console.log(result)

// 简写三:如果一个箭头函数,只有一行代码,并且返回一个对象,这个时候如何编写简写
var bar = () => {
  return {name: 'why', age: 18}
}

var bar = () => ({ name: 'why', age: 18})

箭头函数不使用this的四种标准规则(也就是不绑定this),而是根据外层作用域来决定this

 this面试题:

var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  }
};
function sayName() {
  var sss = person.sayName;
  sss(); 
  person.sayName(); 
  (person.sayName)(); 
  (b = person.sayName)(); 
}
sayName();

var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },
  foo4: function () {
    return () => {
      console.log(this.name)
    }
  }
}

var person2 = { name: 'person2' }

person1.foo1(); 
person1.foo1.call(person2); 

person1.foo2();
person1.foo2.call(person2);

person1.foo3()();
person1.foo3.call(person2)();
person1.foo3().call(person2);

person1.foo4()();
person1.foo4.call(person2)();
person1.foo4().call(person2);
var name = 'window'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  },
  this.foo2 = () => console.log(this.name),
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  },
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo1()
person1.foo1.call(person2)

person1.foo2()
person1.foo2.call(person2)

person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)

person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)
var name = 'window'
function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.obj.foo1()()
person1.obj.foo1.call(person2)()
person1.obj.foo1().call(person2)

person1.obj.foo2()()
person1.obj.foo2.call(person2)()
person1.obj.foo2().call(person2)

面试题的解析:

this指向https://mp.weixin.qq.com/s/hYm0JgBI25grNG_2sCRlTA

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值