【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'); // true

person1.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'

我们可以通过instanceofnew.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 查看一下内容

【JS】es5 es6+ 类相关知识总结
如图: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

// }

【JS】es5 es6+ 类相关知识总结

2、实例

const person = new Person();

person instanceof Person; // true

person.constructor === Person; // true

我们在类的原型上添加属性,则其实例则会‘继承’该属性:

Person.prototype.type = 'class';

person.type; // class

【JS】es5 es6+ 类相关知识总结

3、__proto__ 隐式原型

实例通过 __proto__ 来访问原型

Person.prototype === person.__proto__

【JS】es5 es6+ 类相关知识总结

4、constructor 构造函数

构造函数通过 prototype 访问原型;
原型通过 construcor 访问构造函数;

Person.prototype.constructor === Person; // true

【JS】es5 es6+ 类相关知识总结

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、原型链

【JS】es5 es6+ 类相关知识总结

结语

本文主要为了简单整理一下构造函数的相关知识,只做学习交流。文中难免出现错误,若发现问题还请及时指出,感谢!

文中提到了 hasOwnProperty() in 等方法和操作符,这些属于对象相关的知识点,本文并未做详细解释,后期会更新相关文章,感兴趣可以先看一下JavaScript 原型链常用方法这篇文章;本文也没有介绍继承相关的知识,限于篇幅,后期后单独写一篇文章进行介绍。

最后再强调一下,发现有问题和错误的请一定要指出来,大家一起交流学习

参考资料:

《图解原型和原型链》 JS菌

《ES6 class》

以上是 【JS】es5 es6+ 类相关知识总结 的全部内容, 来源链接: utcz.com/a/91013.html

回到顶部