-
Notifications
You must be signed in to change notification settings - Fork 442
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ES6 Class Methods 定义方式的差异 #67
Comments
// 方式二
class B {
print = () => {
console.log('print b');
}
} 有个小细节,上面这种写法ES6里面应该没有吧,应该只是静态属性的提案中的写法,在使用babel时,如果plugins中只有transform-es2015-classes没有transform-class-properties的话,会报错的。 |
@towersxu es6 官方提案目前没有 但在实际应用中 这种写法很常见了 所以一般都会配置对应的 babel plugin
|
提案 已经处于 s3 箭头函数的写法就类似给类定义了 public method fields 是需要配置 transform-class-properties |
“转换后的 class A 就是一个函数,所以理论上就可以把 A 当作函数调用,但 _classCallCheck 的作用就是禁止将类作为函数调用”。这里应该改成禁止将类当作普通函数调用更合理。毕竟是当作构造函数调用呀。 |
@lz-lee 已纠正 |
我觉得class不仅仅是个语法糖,应该还是加了一些东西的。
|
厉害 |
'use strict';
class Animal {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName = function(){console.log(this.name);};
sayAge = ()=>{console.log(this.age);}
}
let tom = new Animal('tom',19);
let [sayName, sayAge] = [tom.sayName, tom.sayAge];
sayAge(); // 正常输出19
sayName(); // this为undefined 我明白字段函数是属于对象本身而不是原型的,但我不明白为什么箭头函数能绑定this值???? |
箭头函数作用域问题 |
'use strict';
let tom = {
name: 'tom',
age: 19,
sayName: function(){console.log(this.name);},
sayAge: ()=>{console.log(this.age);},
};
let [sayName, sayAge] = [tom.sayName, tom.sayAge];
sayAge(); // this指向外面的空对象{},输出undefined
sayName(); // 抛出错误,this为undefined 那为什么这里面的 |
@ChaosGT 箭头函数的this是在定义时就决定的,你的代码中 |
@beiatisi 这跟作用域完全没关系。 我仔细想了一下,自己尝试回答一下自己的问题: class Animal extends BaseClass {
constructor() {
//构造函数的执行过程大概如下:
let animal = {};
animal.__proto__ = Animal.prototype;
super.constructor.call(animal);
初始化字段(字段属于animal,而不是Animal.prototype)
用户自定义初始化语句
return animal;
}
} 引擎在初始化字段过程中,对箭头函数,会用animal进行替换,导致绑定this。使用函数重写Animal如下: function Animal(){
let animal = {};
animal.__proto__ = Animal.prototype;
//如果Animal是继承来的,还需要调用原型链上的构造函数
//Animal.prototype.__proto__.constructor.call(animal);
animal.name = 'animal';
animal.age = 17;
animal.sayName = ()=>console.log(animal.name); //因为是字段,可以确定属于animal,因此可以直接绑定
return animal;
}
Animal.prototype.sayAge = function(){
console.log(this.age); // 属于原型链上的方法,不能直接确定到某个对象
}
Animal.prototype.__proto__ = BaseClass.prototype; //继承
let tom = new Animal();
let [sayName, sayAge] = [tom.sayName, tom.sayAge];
sayName(); //输出animal
sayAge(); //出错,this为undefined |
@ChaosGT 用 babel 把上面你写的这段代码编译到 ES5 看一下就能明白了 var Animal = function Animal(name, age) {
var _this = this;
_classCallCheck(this, Animal);
_defineProperty(this, "sayName", function () {
console.log(this.name);
});
_defineProperty(this, "sayAge", function () {
console.log(_this.age);
});
this.name = name;
this.age = age;
}; 注意两个_defineProperty 的回调函数内对于 this 的使用方式。一个是直接用了 this,一个用了构造实例的闭包,所以一个 this 会丢失(成为undefined),而另一个不会。 |
@phyzess 感谢你给出的babel结果,这样就很清楚了,说到底还是解释器硬绑定,和语法本身关系不大,虽然很方便,但个人觉得这破坏了语言的统一性。 |
同意。 |
引言
在 JavaScript 中有两条不成文的说法:
因而函数不仅是一等公民,也是具有属性的特殊对象。这一点,也可以从原型链上得到佐证:
函数是继承自
Object
的,因而函数也具备toStirng
、valueOf
等方法。因为函数是对象,所以在 ES6 之前,JavaScript 中的 OOP 编程则纯粹是基于函数的,直到 ES6 提供了class
、super
以及extends
等关键字,不仅精简了语法,也使得 OOP 的编程形式逐渐趋近于 Java/C++ 等语言。class 的背后
ES6 虽然提供了
class
等关键字,但只是语法糖,JavaScript 的 OOP 编程仍然是基于函数的,继承则是基于原型的。看一个示例:
上述代码经过 babel 转换之后:
可以看到,转换后的 class A 就是一个函数,所以理论上就可以把 A 当作函数调用,但
_classCallCheck
的作用就是禁止将类作为普通函数调用:然后看下
_createClass
都做了什么:通过上述代码可知,
_createClass
的功能主要是通过Object.defineProperty
定义了类的普通属性和静态属性。需要注意的是普通属性是定义在了类的原型对象上,静态属性是定义在了类本身上。所以,类 A 的定义就等同于如下代码:
两种定义 Methods 的方式
ES6 中有两种常见的定义 Methods 的方式:
咋一看,二者没什么区别。方式一是常规方式,方式二是通过箭头函数来定义方法,如果你写过 React 应用,应该接触过这种方式。
区别1:this 的绑定
在箭头函数出现之前,每个新定义的函数都有它自己的
this
值,但箭头函数不会创建自己的this
,它从会从自己的作用域链的上一层继承this
。举个粟子:当点击
div
元素时,会触发testClick
,该方法会输出当前的this
,而(严格模式下)此时输出的this
值是undefined
,显然这不是我们要的结果。怎么修改呢?这里至少有三种修改方式,其中之一就是通过箭头函数来定义方式。区别2:继承
先看方式一的继承:
对于上述结果的输出应该没有什么疑问,这是符合我们预期的。然后看下另一段代码:
上述的输出会是什么呢?按照常规思路,应该是先输出
print b
,再输出print d
,但其实不是的。上文有提到,类的继承依然是基于原型的。上文也分析过 babel 转换过的代码,常规的写法中,类的非静态属性都是定义在类的原型对象上,而不是类的实例上的。但箭头函数不一样,通过箭头函数定义的方法时绑定在
this
上,而this
是指向当前创建的类实例对象,而不是类的原型对象。可以查看类 B 转换后的代码:可以看到,
print
方法是定义在this
上的,而不是定义在B.prototype
上。类
D
继承B
,不仅会继承类B
原型上的属性和方法,也会继承其实例上的属性和方法。那么,此时类D
等效的伪代码如下:综上,当
d.print()
执行时,只会输出print b
,而不会输出print d
。因为当访问一个对象实例的属性时,会先在实例上进行查找,如果没有,则顺着原型链往上查找,直到原型链的顶端。若在实例上查找到对应属性,则会返回,停止查找。即使原型上定义了同一个属性,该属性也不会被访问到,这种情况称为"属性遮蔽 (property shadowing)"。
<正文完>
相关资料
The text was updated successfully, but these errors were encountered: