- 为什么要使用this
- this 的使用误解 (
2 种 指向误解
) - this 和词法作用域的比较
- this 的指向正解
- this 的绑定方法(
call()、apply()、bind()
)
this 总是返回一个对象( fun 也是一个对象 ) + this 指向总是善变的
this 的三篇文章按照顺序复习
this基本概念 -> this绑定规则 -> this 应用实例
基本概念
this 总是返回一个对象 即包含当前属性或者方法函数的对象
, 因为对象的属性和方法函数可以赋予另一个对象,所有 this 指向的对象也是善变的。
function foo () {
console.log('name :' + this.name);
}
var obj = { name: "obj1", foo: foo };
var obj2 = { name: "obj2", foo: foo };
var obj3 = { name: "obj3" };
obj.foo() // obj1
obj2.foo() // obj2
- 为什么 使用this
-
this 提供了一种优雅的方式来
传递
一个对象的引用, 使用 this 能够得到更加 简介的 api.显式的传递对象的引用(eg: 函数参数传递) 使得代码变得混乱和难以维护(eg: 传递参数的个数就是个问题)。
-
使用 this 可以
自动引用合适的上下文环境
,并且确定this当前指代的对象
进而使用其属性和方法函数
-
** 我们使用 this 的最终目的是保证能够
正确 + 便捷
的使用 对象的属性和方法函数处理数据 **
-
this 的使用误解 ( this 的指向误解 )
-
this 并不指向函数自身
-
this 不一定指向定义它的函数作用域或者定义时的作用域
function foo() { var a = 2; this.bar = function() { console.log(this.a) } this.bar(); } foo();
-
-
this 和词法作用域的比较
this 可以通过
参数传递
或者直接的对象引用
来代替。** 但是 this 因为有其更加灵活的特性才被大家所使用,总是使用词法作用域容易让你 回到编程的舒适区 **
我们来记录 foo 函数被调用的次数
function foo(num) {
console.log('foo ' + num);
// 记录 foo 函数被调用的次数
this.count++;
}
foo.count = 0;
for(var i = 0; i < 5; i++) {
foo(i);
}
// 记录 foo 函数被调用的次数
console.log(foo.count);
分析一下上面的 输出 的值 和 this 的指向?
若需要你来改造 你应该怎么办??
-
this 的指向正解 ( 请看 this 的 四种绑定策略 )
函数内部的this 总是指向 函数本身被调用的位置。(
this 绑定规则有详解
)所以对于 this 的 指向就是 寻找函数的调用位置为首。(浏览器自带的 开发者工具可以很方便的查看函数调用栈)
-
this 的 三种
强制绑定
的 方法详解
** javascript 提供了 call apply 和 bind 三个方法,用于固定 this 的指向 **
** call 和 apply bind 都是定义在 Function 上面的方法。**
** call apply 返回一个调用函数的执行结果。 **
** bind 返回一个修改了内部this的 包装函数。 **
-
call 的 传参解析 ( 4 种传参的方式 )
call 参数使用正常的对象
call 参数为空、null、undefined 则默认传入全局对象
var n = 'bar'; var obj = {n: 'foo'} function baz() { console.log(this.n) } baz.call(obj); // foo baz.call(window); baz.call(); baz.call(null), baz.call(undefined);
call 传入一个基本类型的数据,这个基本类型的数据会被转为包装对象 赋值给 this
var f = function () { console.log(this); }; f.call(5) // Number {[[PrimitiveValue]]: 5}
call 可以传递多个参数,第一个参数为需要绑定的对象,后面的参数 依次是函数调用的传参参数
-
apply ( 接受一个数组作为函数执行时的参数 )
-
bind 将函数体内部的 this 强制绑定,返回一个新的经过包装的函数
// 自己实现的简单功能的 bind 函数 function bind(fun, obj) { return function() { // 这个 就是 bind 返回的函数 fn.apply(obj); } }
apply call bind 使用全攻略 ( 4种使用场景 )
-
传递空对象 ** 参数柯里化 **( 看你传递的对象到底有多空 )
call apply bind 传递一个空对象常常用于参数的柯里化
function bar(a, b) { console.log(a + ',' + b); }
直接使用 null / undefined 作为空 传入
var baz = bar.call(null, 1, 4) // 1, 4 var bak = bar.bind(null, 10); bar(19) // 10,19
传递进去一个 真空对象 var ø = Object.create(null);
var baz = bar.call(ø, 1, 4) // 1, 4
-
自定义的两种 bind 方法
-
创建自定义的 bind 方法函数
function bindCopy(fn, obj) { return function() { fn.apply(obj, arguments); // 这里主要依赖 call 方法 ** 注意不能少了函数参数 ** } }
-
扩展函数方法库(** 重要知识点 **)
Function.prototype.bindCopy() { var fn = this; var obj = arguments[0]; var args = Array.prototype.slice(arguments, 1); return function() { fn.apply(obj, args) } }
重要知识点:
-
函数使用 prototype 可以进行扩展( Function.ptototype.bindCopy )
-
使用 apply 可以将 类数组对象( 有 length 属性的对象 ) 进行参数结构
-
函数自身调用时也是使用 this 表示调用的上下文
-
注意 原函数本身的 参数不能少
-
-
-
apply 和 call 的妙用
-
将数组解构 (上面是将类数组对象解构)
var arr = [1, 4, 6, 7]; Math.max.apply(null, arr); // 7
-
使用 call 方法用在 对象继承中 重新调用被子类覆盖的父类方法;
function Parent() { this.a = 'super'; this.Super = function() { console.log('父类的 Super 方法' + this. a); } } var p = new Parent(); function Children (a) { this.a = a; Parent.call(this); // 调用 父类的构造函数 } var c = new Children(12); c.Super() // 使用子类 继承过来的方法 c.Super.call(p) // 使用 call 重新调用父对象的方法
试着自己实现一个类似 于 forEach 的 方法吧
-
.
网友评论