【JS】es5 es6+ 类相关知识总结
一、类
1、类的声明&实例化
es5:
// 声明function Person() {}
// 实例化
var person = new Person();
es6:
// 声明class Person {}
// 实例化
const person = new Person();
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
2、实例方法、实例属性、原型方法、原型属性、静态方法、静态属性
es5:
function Person() { // 构造函数// 实例属性
this.name = '张三';
// 实例方法(本质也是实例属性,getName为函数表达式,赋值给this.getName)
this.getName = function() {
return this.name;
};
}
// 静态静态属性
Person.age = 1;
// 静态方法
Person.getAge = function() {
return this.age;
}
// 原型属性
Person.prototype.sex = '男';
// 原型方法
Person.prototype.getSex = function() {
return this.sex;
}
var person1 = new Person();
var person2 = new Person();
es6:
class Person { // 类,本质也是构造函数constructor {
this.name = '张三'; // 实例属性
this.getName = function() { // `实例方法` 严格来说也是实例属性,将方法赋值给属性getName
return this.name;
};
}
// name = '张三'; // 实例属性 等价于上面在constructor中的写法
// getName = function() { // 实例方法
// return this.name;
// }
static age = 1; // 静态属性(tips: 该写法之前只是提案,但笔者在chrome浏览器测试可用)
static getAge() { // 静态方法
return this.age; // 注意静态方法的this指向的是类本身,而非实例
}
getSex() { // 原型方法
return this.sex;
}
}
es5 与 es5 对比
class P {fn() {
...
}
}
// 上面的方法等同于
P.prototype = {
function fn() {
...
}
}
class P {constructor(){ // constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。
// ...
}
}
Object.assign(Point.prototype, {
fn1(){},
fn2(){}
});
prototype对象的constructor属性,直接指向“类”的本身,这与ES5的行为是一致的。
P.prototype.constructor === P
另外,类的内部定义的所有方法,都是不可枚举的(non-enumerable)。这一点与ES5的行为不一致。
function P() {...
}
P.prototype.fn = function(){}
Object.keys(P.prototype) // ["fn"]
Object.getOwnPropertyNames(P.prototype) // ["constructor","toString"]
class P {
constructor(x, y) {
// ...
}
fn() {
// ...
}
}
Object.keys(P.prototype) // []
Object.getOwnPropertyNames(P.prototype) // ["constructor","fn"]
const fnName = "fn";class P {
constructor() {
// ...
}
[fnName]() {
// ...
}
}
Object.getOwnPropertyNames(P.prototype) // ["constructor", "fn"]
2.1 实例对象
person1.hasOwnProperty('name'); // trueperson1.hasOwnProperty('getName)'; // true
person1.name; // 张三 - 实例可以直接访问实例属性
person1.name = '张四';
person2.name; // 张三 - 修改实例属性不会影响其他实例
es5:function P(){}
var p = P() // underfind
es6:
class P {}
const p = P() // Uncaught TypeError: Class constructor P1 cannot be invoked without 'new'
我们可以通过instanceof
或new.target
来保证没有new关键字,同样可以创建类的实例(非class方式)
es5:// instanceof
function P() {
if(!this instanceof P) {
return new P(arguments);
}
}
// new.target
function P() {
if(new.target !== P) {
return new P(arguments);
}
}
2.2 原型对象
Person.prototype // {sex: "男", getSex: ƒ, constructor: ƒ}Person.prototype.hasOwnProperty('sex') // true
Person.prototype.hasOwnProperty('getSex') // true
Person.prototype.sex; // 男 构造方法可通过原型`prototype`进行访问原型属性
person1.__proto__ === person2.__proto__ // true
person1.sex; // 男
person2.sex; // 男
person1.__proto__.sex = '女';
person2.sex; // 女;
person1.getSex = function() {
return '男';
}
person1.getSex(); // 男
person2.getSex(); // 女 这里的表现和上面不一样,因为 person1.getSex = ... 的方式只是为person1实例添加了一个实例方法,并未作用到原型链上,故不会共享到person2上
// 我们可以打印一下person1 查看一下内容
如图:person1实例上新增了一个getName方法,该方法就是我们通过person1.getSex添加的,而其原型链__proto__
上面的getSex则为实例所共享的原型方法;访问的时候,会先在实例本身查查找要访问的属性,若不存在,则会在其原型__proto__
上继续查找,直至__proto__
为null,这就是原型链;关于原型链本文后续会详细介绍;
2.3 静态对象
person1.age; // undefind - 类的实例无法获取静态属性person1.getAge; // undefind - 类的实例无法获取静态方法
Person.age; // 1 - 静态属性只能通过构造函数本身访问
Person.getAge; // 1 - 静态方法只能通过构造函数本身访问
es6中,静态方法可以通过static
关键字声明,但通过static
声明属性目前只是提案
class P {constructor(){ ... };
static fn () { console.log('parent') };
}
class C extends P {
constructor(props) {
super(props);
}
static getParentStaticFn () {
}
}
const p = new P();
p.fn(); // TypeError: (intermediate value).fn is not a function
P.fn(); // parent
C.getParentStaticFn(); // parent
静态方法的this
指向类本身
二、原型链
1、prototype 原型
原型与构造函数的关系就是,构造函数内部有一个名为 prototype 的属性,通过这个属性就能访问到原型:
class Person {}Person.prototype
// {
// constructor: class Person
// __proto__: Object
// }
2、实例
const person = new Person();person instanceof Person; // true
person.constructor === Person; // true
我们在类的原型上添加属性,则其实例则会‘继承’该属性:
Person.prototype.type = 'class';person.type; // class
3、__proto__
隐式原型
实例通过 __proto__ 来访问原型
Person.prototype === person.__proto__
4、constructor 构造函数
构造函数通过 prototype 访问原型;
原型通过 construcor 访问构造函数;
Person.prototype.constructor === Person; // true
5、实例、构造函数、原型之间的关系
Person // 构造函数person // 实例
person = new Person
Person.prototype // 原型
person.__proto__ // 隐式原型
Person.prototype === person.__proto__
Person.prototype.constructor === Person
person.__proto__.constructor === Person
在读取一个实例的属性的过程中,如果属性在该实例中没有找到,那么就会循着 __proto__
指定的原型上去寻找,如果还找不到,则尝试寻找原型的原型:
class P {constructor() {
this.name;
}
setName(value) { // ES6 方法被挂在到prototype上而非类本身
this.name = value;
}
getName = () => { // 箭头函数则挂在到对象本身
return this.name;
}
}
const p = new P();
6、原型链
结语
本文主要为了简单整理一下构造函数的相关知识,只做学习交流。文中难免出现错误,若发现问题还请及时指出,感谢!
文中提到了 hasOwnProperty() in 等方法和操作符,这些属于对象相关的知识点,本文并未做详细解释,后期会更新相关文章,感兴趣可以先看一下JavaScript 原型链常用方法这篇文章;本文也没有介绍继承相关的知识,限于篇幅,后期后单独写一篇文章进行介绍。
最后再强调一下,发现有问题和错误的请一定要指出来,大家一起交流学习
参考资料:
《图解原型和原型链》 JS菌
《ES6 class》
以上是 【JS】es5 es6+ 类相关知识总结 的全部内容, 来源链接: utcz.com/a/91013.html