假如在 js 中去掉 this
,会怎么样?
- 全局函数 -> 直接写
window
嘛,干嘛用this
- 箭头函数 -> 在下内部本就没有
this
,还是按照闭包的规则,该是谁就是谁 bind
、apply
、call
-> 在下本就是针对this
的元编程,自然也不再需要,别了您内- 构造方法,对象方法 -> (尴尬)
其实 js 中的 this
,设计的目的便是实现看起来像 java 的 oop 模式。
const a = new foo()
a.bar()
上述代码中,作为构造函数的 foo
内 this
应该指向一个新的对象并作为结果返回,而作为方法的 bar
应该访问的是其对应的实例对象 a
。
让我们再瞟一眼声明
function foo() {console.log(this)}
function bar() {console.log(this)}
// 在某不知名的角落里
foo.prototype.bar = bar
在声明的时候我哪知道谁是构造函数,谁是实例方法(掀桌)。
好吧,那只能看函数执行时动态确定了,用 new [fn](...args)
执行的是构造函数,用 obj.[fn](...args)
执行的是对象方法。以此确定 this
的指向。
如此看来在函数中,本就不该访问 this
,应该报错。在拥有闭包作用域的 lambda 表达式中,this
应该按闭包的规则向上层查找。
慢着,js 的函数本身就带闭包作用域那咋办?
啊啊啊,不管了, 直接就指向 window
吧,本就一周设计出来的玩意,还想咋地,又不是不能用……
js 相比其他很多语言拥有更高度的动态性,而这种动态性并非都是优势,一些来自于过于敷衍的设计的动态性,往往会造成各种灾难,this
便是一例。
this 这种灾难来自于设计者想把全局函数、lambda 表达式、构造函数、对象上的方法糅合在一起。然而这并无必要。
为什么其他语言不会有这种问题,因为其他语言把全局函数、lambda 表达式、构造函数、对象上的方法声明时分的很清楚,并不需要动态去确定。
以 rust 举例
pub struct ClassName {
field: i32,
}
impl ClassName {
// 充当构造函数的静态方法 new,返回一个 ClassName 实例
pub fn new(value: i32) -> ClassName {
ClassName {
field: value
}
}
pub fn public_method(&self) {
// 方法内显示声明的 self 即是 js 内部隐式声明的 this
println!("from public method");
self.private_method();
}
fn private_method(&self) {
println!("from private method");
}
}
而 scala 的设计显然更为精妙。
// class 的声明代表这是一个构造函数
class Point(xc: Int, yc: Int) {
// 构造函数中声明的变量/常量就是对象的属性
var x: Int = xc
var y: Int = yc
// 构造函数中声明的函数就是对象的方法
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
println ("x 的坐标点: " + x);
println ("y 的坐标点: " + y);
}
}
今时今日的 js 已经不再是那个 js 了。在 this
问题上,TC39 委员会又做了那些努力呢。
首先,箭头函数替代了履行了 function 中 lambda 表达式的职能。在箭头函数中访问 this
,也会以闭包的方式逐层向上查找了。var _this = this
已经成为了历(li)史(shi)。
全局函数中的 this
,其实在严格模式中已经解决了一部分。
而真正应该用到 this
的地方即构造方法和实例方法,全都在 class
声明的括号内部。
也就说一般情况下,在我们的代码中, this
只能出现在 class
的括号内。
class Point {
constructor(x, y) {
// 显示声明的构造函数
this.x = x;
this.y = y;
}
toString() {
// 显示声明的对象方法
return '(' + this.x + ', ' + this.y + ')';
}
}
为啥整篇文章避而不谈 call/apply/bind
。因为在我看来这仨都属于 js 元编程的范畴。
何谓元编程?
简而言之,就是在代码层面上赋予新的解释,针对代码进行编程。可以是由原来的代码生成新的代码,也可以是改写原代码的功能。
例如 proxy
就是让用户改写对象上的操作,call/apply/bind
则是改写函数内部的动态作用域规则(由于箭头函数内没有动态作用域,所以对它没用), with
那更是厉害到让人闻风丧胆。
而元编程则是业务代码中不允许出现,框架代码中慎之又慎的东西。
我希望在你用到的时候已经不需要这篇文章了。
时下,不少人选择以面向面试题学习作为自己的学习方式。然而实际上应试教育和实际开发是有比较大的出入的。
譬如 this
,是前端基础面试题中的常客,同时在哪都有汗牛充栋的的文章一一列举 this
在不同的上下文环境中的指向的文章。有些文章甚至想出各种“花式”技巧,什么就近原则啊,什么各种嵌套啊。有用吗?私以为并没有啥用。反倒是那些能真的讲讲 this 为什么如此设计,以及如何应用的文章寥寥。
下一篇,打算聊聊绝大多数人都没在代码里写过的 WeakMap
WeakSet
,如果大家觉得本文还有点内容,还请高抬贵手点个赞哈。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!