一、this
一、函数的调用方式决定了 this 的指向不同:
在 JavaScript 中 this 关键字一般指的是 函数调用时 所在的 环境上下文 ,存储了 环境上下文对象的内存地址 ,根据函数的调用的方式不同 ,其 this 会指向不同的对象 ,我们可以通过 this 关键字在函数内部中操作其指向的对象
要注意:
this的绑定和函数声明的位置没有任何关系,只取决于函数的调用位置和调用方式;
this绑定规则有4点:按优先级1到4判断**
在调用中,this一般指向调用它的函数,直接调用全局作用域中的 say 函数的时候等价于 window.say() 因此 ,全局作用域中的 say 函数中的 this 指向的就是 window 对象 。
1.普通函数调用,此时 this 指向 window
function fn() {console.log(this); // window}fn(); // window.fn(),此处默认省略window
2.构造函数调用, 此时 this 指向 实例对象
function Person(age, name) {this.age = age;this.name = nameconsole.log(this) // 此处 this 分别指向 Person 的实例对象 p1 p2}var p1 = new Person(18, 'zs')var p2 = new Person(18, 'ww')
对象方法调用, 此时 this 指向 该方法所属的对象
var obj = {fn: function () {console.log(this); // obj}}obj.fn();
通过事件绑定的方法, 此时 this 指向 绑定事件的对象
<body><button id="btn">hh</button><script>var oBtn = document.getElementById("btn");oBtn.onclick = function() {console.log(this); // btn}</script></body>
二、构造函数中的this
// 构造函数的定义function Person(name, age, gender) {this.name = name;this.age = age;this.gender = gender;this.say = function() {console.log('我的名字是 ' + this.name + ' ,今年' + this.age + '岁');}}// 通过构造函数创建 person 对象var person = new Person('momo', 18, '男');person.say(); // 打印:我的名字是 momo ,今年18岁
在使用 new 关键字调用构造函数后 ,会在堆内存空间中创建一个新的对象 ,然后构造函数中的 this 就储存了堆空间这个新的对象内存地址 ,最后会默认返回这个 this 。
三、修改函数中的 this 指向
可以通过 call() 、 apply() 和 bind() 函数,修改函数中 this 指向。
/* =============== call 与 apply ============ */function say(a, b) {console.log('我是' + this.value + ' ' + a + ',' + b);}var red = {value: '红色',redSay: say};var green = {value: '绿色',greenSay: say};red.redSay(1, 2); // 我是红色 1,2green.greenSay(3, 4); // 我是绿色 3,4// 将 redSay 函数中的 this 指向改为 green 对象red.redSay.call(green, 1, 2); // 我是绿色 1,2red.redSay.apply(green, [3, 4]); // 我是绿色 3,4
apply() 与call()非常相似,不同之处在于提供参数的方式,apply()使用参数数组,而不是参数列表
call(要指向的对象,参数) apply(要指向的对象,数组参数)
对于bind() 函数,看下面这个例子
window.name = 'window';var person = {name: 'momo',say: function(a, b) {console.log('我的名字是' + this.name + ' ' + a + ',' + b);}}var mSay = person.say;// 丢失了 person 对象的 thismSay(1, 2); // 我的名字是window 1, 2// 重新给 mSay 的 this 绑定为 person 对象mSay = mSay.bind(person);// 此时 mSay 中的 this 就是 person 对象了mSay(1, 2); // 我的名字是momo 1, 2// 下面的写法与上面等价mSay.bind(person, 1, 2)();mSay.bind(person)(1, 2);
使用 bind() 来修改函数的 this 的时候并不会执行该函数 ,而是 返回一个新的函数对象 ,这个新的函数对象中的 this 被修改为了指定的对象 ,其余的函数体内部代码与修改前的一样 。在 ES6 中箭头函数内部的 ``this`` 指向的是箭头函数定义时的上下文对象 ,不由调用它的对象来决定。
二、闭包
一、概念
闭包是一个对象 ,其存在于内部函数对象中 ,保存了内部函数所使用的外部函数中定义的数据
产生条件:
- 函数嵌套 。
- 内部函数使用了在外部函数中定义的数据 。
- 指执行了外部函数 。
流程:首先在一个 函数嵌套 的场景下 ,并且 内部函数使用了外部函数定义的数据 ,然后再 执行外部函数 ,当代码执行到 内部函数定义完毕 时 ,此时内部函数中就已经生成了一个闭包对象 ,其 存储了内部函数使用的外部函数中定义的数据
function fn1() {var a = 3;// 当 fn2 函数对象定义完毕时 ,其内部产生了闭包对象function fn2() {console.log(a);}return fn2;}// 调用 fn1 函数 ,将 fn2 函数对象的内存地址赋值给 fn3 对象var fn3 = fn1();// 中断 fn3 于 fn2 对象之间的引用 ,fn2 被 GC 回收 ,闭包对象死亡fn3 = null;
闭包的死亡:
在 堆区的内部函数对象没有被栈区的变量引用 时 ,此时堆区的内部函数对象就会被 GC 当作垃圾数据回收 ,同时存在于内部函数对象中的闭包对象就会死亡 。
二、应用
延长了局部数据的存活时间
function fn1() {let a = 3;function fn2() {a++;console.log(a);}return fn2;}var fn = fn1();fn(); // 4fn(); // 5
在 fn1 函数执行完毕后 ,其内部的局部变量 a 已经被释放 ,但是由于闭包机制的存在 ,fn2 函数对象保存了这个局部变量的数据 ,延长了局部数据的存活时间
