美文网首页
内层管理后续和this的绑定规则 JS

内层管理后续和this的绑定规则 JS

作者: 咸鱼不咸_123 | 来源:发表于2022-04-20 09:44 被阅读0次

1.闭包的内存泄露

function createFnArray(){
  /**
   * B KB MB GB TB 进制转换是1024
   */
  var arr=new Array(1024*1024).fill(1); //这个空间占据了4KB
  return function(){
    console.log(arr.length);
  }
}
var arrayFn=createFnArray();
20.png

执行完createFnArray函数后,FEC函数执行上下文栈会移除ECStack栈

21.png 22.png
  • 因为GC使用的算法是清除标记,只要是根对象能够访问到的对象或内存空间,就不会被销毁,那些不可达的对象就会被销毁

以上AO:0Xa002的arr指向的内存空间区域就会一直不会被销毁,所以就会产生内存泄露

以上是一个小的内存泄露,下面是更严重的内存泄露

function createFnArray(){
  /**
   * B KB MB GB TB 进制转换是1024
   */
  var arr=new Array(1024*1024).fill(1); //这个空间占据了4KB
  return function(){
    console.log(arr.length);
  }
}
var arrayFn=[];
for(let i=0;i<100;i++){
  arrayFn.push(createFnArray())
}
function createFnArray(){
  /**
   * B KB MB GB TB 进制转换是1024
   */
  var arr=new Array(1024*1024).fill(1); //这个空间占据了4KB
  return function(){
    console.log(arr.length);
  }
}
// var arrayFn=createFnArray()
var arrayFn=[];
for(let i=0;i<100;i++){
 setTimeout(()=>{
  arrayFn.push(createFnArray());
  console.log("push:",i);
 },i*100)
}

setTimeout(()=>{
  for(let i=0;i<50;i++){
    setTimeout(()=>{
      arrayFn.pop()
      console.log("pop:",i);
    },i*100)
  }
},10000)

2.闭包引入的AO变量属性的销毁

function foo(){
  var name="foo";
  var age=20;
  function bar(){
    debugger
    console.log(name);
  }
  return bar
}
var fn=foo()
fn()
/**
 * ECMA规范:只有内层函数(作用域)使用了外层作用域的变量,那么外层作用域就不会被销毁
 * V8引擎为了进一步优化 ,会将不使用的AO中的变量会销毁
 */
23.png
  • ECMA规范:只要内层作用域引用了外层作用域的变量,那么那个AO对象就不会被销毁,AO中的所有变量也不会被销毁,
  • V8引擎做了进一步优化,它会将AO中没有引用到的变量销毁,引用的变量保留。例如以上浏览器的截图,name被保留了,但是age被销毁了

3.为什么需要this

在常见的编程语言中,几乎都有this这个关键字,但是javascript的this和常见的面向对象有点不太一样

  • 常见的面向对象的编程语言中,比如java、c++、Swift、Dart等等一系列语言中,this一般用在类方法中
  • 也就是你需要创建一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象
  • 但是javascript的this更加灵活,无论是它出现的位置还是它代表的含义
var obj={
  name:"wjy",
  eating:function(){ //方法
    console.log(this.name+"在吃东西");
  },
  running:function(){//方法
    console.log(this.name+"在跑");
  },
  study:function(){//方法
    console.log(this.name+"在学习");
  }
}
obj.eating();
obj.running();
obj.study()

但是其实不使用this也可以使用

var obj={
  name:"wjy",
  eating:function(){ //方法
    console.log(obj.name+"在吃东西");
  },
  running:function(){//方法
    console.log(obj.name+"在跑");
  },
  study:function(){//方法
    console.log(obj.name+"在学习");
  }
}
obj.eating();
obj.running();
obj.study()

但是如果发现obj这个变量名取名不太规范,想要去修改,但是你会发现,不仅是外面使用的obj的地方需要修改,就连对象内部定义的属性和方法调用的地方也要修改,会发现修改的工作量巨大,所以使用this的话会方便很多。

4.this在全局作用域指向什么

this在全局作用域中指向的是window(浏览器中)或空对象(node环境中)

  • 在浏览器中,全局作用域的this指向window
24.png
  • 在node环境中,全局作用域的this指向的是空对象
    • node会将一个文件当作一个module——加载——编译——放入到一个函数中——执行这个函数.call
25.png

5.this在函数中指向

但是,在开发中很少直接在全局作用域下去使用this,通常都是在函数中使用

  • 在函数执行时,会创建一个函数执行上下文
  • 这个上下文会存储函数的调用栈、AO对象
  • this也是其中的一个记录
26.png
  • this是动态绑定的
  • scope-chain是在编译的时候就已经确定的
function foo(){
  console.log(this);
}
foo();//1. 直接调用
// 创建一个对象,对象中的函数调用指向foo
var obj={
  name:"wjy",
  foo:foo,
}
obj.foo();//2. 调用对象的foo函数


foo.apply("abc") //3. 使用apply绑定this
27.png

这个案例可以给我们一个什么样的启示呢?

  • 函数在调用时,javascript会默认给一个this绑定一个值
  • this的绑定和所处的位置没有关系的
  • this的绑定和调用方式调用的位置有关
  • this是在运行时被绑定的

那么this到底是怎样的一个绑定规则呢?

  • 默认绑定
  • 隐式绑定
  • 显示绑定
  • new 绑定

6.绑定规则

  • 默认绑定
  • 隐式绑定
  • 显示绑定
  • new 绑定

6.1默认绑定

什么情况下使用默认绑定呢?独立函数调用

  • 独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用
6.1.1 案例1
function foo(){
  console.log(this);
}
foo();//独立函数调用
6.1.2 案例2
function foo1(){
  console.log(this);
}
function foo2(){
  console.log(this);
  foo1();
}
function foo3(){
  console.log(this );
  foo2()
}
foo3()
6.1.3 案例3
var obj={
  name:"wjy",
  foo:function(){
    console.log(this);
  }
}
var bar=obj.foo;
bar();
6.1.4 案例4
function foo(){
  console.log(this);
}
var obj={
  name:"wjy",
  foo:foo
}
var bar=obj.foo;
bar()
6.1.5 案例5
function foo(){
   function bar(){
    console.log(this);
  }
  return bar
}
var fn=foo();
fn()

看函数是如何调用的,只要是独立调用的,this指向的就是全局对象

6.2 隐式绑定

另外一种常见的调用方式就是通过某个对象进行调用的

  • 也就是它的调用位置中,是通过某个对象发起的函数调用。
  • 函数的隐式绑定:object.fn()
    • object对象会被绑定到fn函数的this变量上
6.2.1 案例1
function foo(){
  console.log(this);
}
var obj={
  name:"wjy",
  foo:foo
}
obj.foo()
6.2.2 案例2
var obj={
  name:"wjy",
  eating:function(){
    console.log(this.name+"在吃东西");
  },
  running:function(){
    console.log(this.name+"在跑步");
  }
}
obj.eating()
obj.running()
6.2.3 案例3

这时候fn函数调用的时候this指向的是全局对象,只要函数独立调用,没有通过某个对象进行调用时,采用的就是默认绑定

var obj={
  name:"wjy",
  eating:function(){
    console.log(this.name+"在吃东西");
  },
  running:function(){
    console.log(this.name+"跑");
  }
}
var fn=obj.eating;
fn();
6.2.4 案例4
var obj1={
  name:"obj1",
  foo:function(){
    console.log(this);
  }
}
var obj2={
  name:"obj2",
  bar:obj1.foo
}
obj2.bar()  

6.3 显式绑定

  • 隐式绑定有一个前提条件:

    • 必须在调用的对象的内部有一个对函数的引用(比如一个属性)

    • 如果没有这样的引用,在进行调用时,会报找不到该函数的错误

    • 正是通过这个引用,间接的将this绑定到这个对象上。

如果我们不希望在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?

  • javascript的所有函数都可以使用call和apply方法(这个和prototype有关)

    • 它们两个的区别在这里就不再展开
    • 其实非常简单,第一个参数是相同的,后面的参数,apply为数组,call为参数列表
  • 这两个函数的第一个参数要求是一个对象,这个对象的作用是什么呢?就是给this使用

  • 在调用这个函数时,会将this绑定到这个传入的对象上。

function foo(){
  console.log(this);
}
// foo(); //foo直接调用和foo.call或foo.apply调用是不同在于this的绑定是不同的
/**
 * * 直接调用 this绑定的是全局对象
 * foo.call()或foo.apply()调用:this指定的是第传入的对象
 */
var obj={
  name:"wjy"
}
/**
 * * call和apply是可以指定this的绑定
 */
foo.call(obj);
foo.apply(obj)
foo.apply("aabbcc")
6.3.1 call和apply有什么区别?

call和apply是可以明确绑定this,这个绑定规则称之为显式绑定

  • call后面的参数 是参数列表,参数与参数之间用逗号分隔
  • apply后面的参数 是数组,将要传入的参数放入到数组中
function sum(num1,num2){
  console.log(num1+num2,this);
}
sum.call("call",20,30);//call后面的参数 是参数列表
sum.apply("apply",[20,30])// apply后面的参数 是数组
6.3.2 bind
function foo(){
  console.log(this);
}
// foo.call("aaa");
// foo.call("aaa");
// foo.call("aaa");
// foo.call("aaa");

var newFoo=foo.bind("aaa");
newFoo()//this绑定的是"aaa"
foo()//this绑定的是window

默认绑定和显式绑定冲突时,显式绑定优先级别高一些

6.4 new绑定

  • javascript中的函数可以当做一个类的构造函数来使用,也就是使用new 关键字

使用new关键字来调用函数是,会执行如下的操作:

  1. 创建一个全新的对象
  2. 这个新对象会被执行prototype连接
  3. 这个新对象会被绑定到函数调用的this上(this的绑定在这个步骤上完成)
  4. 如果函数没有返回其他对象,表达式会返回这个新对象
// 我们通过一个new关键字调用一个函数时(构造器),这个时候this是在调用这个构造器创建出来的新对象
function Person(name,age){
  this.name=name;
  this.age=age
}

var p1=new Person("wjy",22);
var p2=new Person("hyz",22);
console.log("p1:",p1);
console.log("p2:",p2);

7.总结

内存管理和this.png

相关文章

  • 内层管理后续和this的绑定规则 JS

    1.闭包的内存泄露 执行完createFnArray函数后,FEC函数执行上下文栈会移除ECStack栈 因为GC...

  • 可视化D3.js库(2)-选择元素和绑定数据

    D3.js库-2-选择元素和绑定数据 选择元素和绑定数据可以说是后续进行D3库操作的基础,所以需要掌握其基本操作 ...

  • Vue.js数据双向绑定实现

    目前的几种主流前端框架中,react是单向绑定,而angular.js和vue.js是双向绑定,实现双向绑定的方法...

  • Element-UI表单验证

    校验规则 表单通过rules属性绑定校验规则对象,表单项通过prop属性绑定具体校验规则 注意校验的字段必须和表单...

  • PYQT5(十一)实现右键菜单

    窗体绑定右键事件 设置右键菜单事件,和后续的动作

  • 你不知道的JavaScript:this绑定规则

    默认绑定 JS中最常用的函数调用类型:独立函数调用。可将其看作无法应用其他规则时的默认规则。 声明在全局作用域中的...

  • 深入了解javascript中的this

    this 作为js中的常用引用,区别与词法作用域,有自己一套的规则 this的绑定规则 函数在执行过程中的调用位置...

  • 【JS】this绑定四个规则

    this绑定四个规则 zhongxia 2016-07-22 10:02:32 出处: you dont konw...

  • DOM的事件绑定、事件监听

    JS有三种常用的绑定事件的方法 在DOM元素上直接绑定 在JS代码中绑定 在JS中绑定事件监听函数 在DOM中直接...

  • JS中this指向

    函数有4种调用方式,对应就有4种绑定规则:默认绑定、隐式绑定、硬绑定和构造函数绑定。 1、默认绑定 当作为普通函数...

网友评论

      本文标题:内层管理后续和this的绑定规则 JS

      本文链接:https://www.haomeiwen.com/subject/xqzmertx.html