class与构造函数
目录
- 回望构造函数
- 来看class写法
- 细微区别
- 将class一比一转换为function写法
回望构造函数
首先回顾es6之前的构造函数写法
// 写一个构造函数
function Student(name, age) {
this.name = name;
this.age = age;
}
// 挂载原型方法
Student.prototype.call = function () {
console.log("my name is " + this.name);
};
// 挂载静态方法
Student.line = function () {
console.log("--------------------------------");
};
const xinxin = new Student('xinxin', 20);
console.log(xinxin.name, xinxin.age);
xinxin.call();
Student.line();
输出如下:
xinxin 20
my name is xinxin
------------------------------------
这种写法是具有二义性的,是通过调用方法来决定它是构造函数还是普通函数的
当我们以普通函数调用也不会报错
console.log(Student('xinxin', 20)); // undefined
反之也是,会得到一个空对象,如下:
function hello(name) {
console.log('hello' + name);
}
const h = new hello('xinxin');
console.log(h); // helloxinxin
// hello {}
来看class写法
和上面使用相同的例子
// 写一个构造函数
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 挂载原型方法
call() {
console.log("my name is " + this.name);
};
// 挂载静态方法
static line() {
console.log("--------------------------------");
};
}
const xinxin = new Student('xinxin', 20);
console.log(xinxin.name, xinxin.age);
xinxin.call();
Student.line();
输出如下:
xinxin 20
my name is xinxin
------------------------------------
细微区别
- class是没有所谓二义性的,所以自然不能用普通函数调用的方法来调用
console.log(Student('xinxin', 20));
报错:Class constructor Student cannot be invoked without ‘new’
类构造器必须要使用new
- 再就是class里为严格模式,不能有相同名字的参数
class Student {
constructor(name, name) {
this.name = name;
}
// 挂载静态方法
static line () {
console.log("--------------------------------");
};
}
const xinxin = new Student('xinxin', 20);
console.log(xinxin.name, xinxin.age);
报错:Duplicate parameter name not allowed in this context
在这个上下文中相同的参数名是不被允许的
来看function声明的则没有这个问题
function Student(name, name) {
this.name = name;
}
const xinxin = new Student('xinxin', 20);
console.log(xinxin.name, xinxin.age); // 20 undefined
- 接下来让我们遍历两种方式创建的实例
function
// 写一个构造函数
function Student(name, age) {
this.name = name;
this.age = age;
}
// 挂载原型方法
Student.prototype.call = function () {
console.log("my name is " + this.name);
};
// 挂载静态方法
Student.line = function () {
console.log("--------------------------------");
};
const xinxin = new Student('xinxin', 20);
for (i in xinxin) {
console.log(i);
}
// name age call
class
// 写一个构造函数;
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 挂载原型方法
call() {
console.log("my name is " + this.name);
};
// 挂载静态方法
static line() {
console.log("--------------------------------");
};
}
const xinxin = new Student('xinxin', 20);
for (i in xinxin) {
console.log(i);
}
// name age
class的原型方法不会被遍历出来,说明ES6 中的原型方法是不可被枚举的, ES6 对此也是做了特殊处理的
- class的方法是不能被new出来的,function声明的方法也还是function依然具有二义性也是可new可直接调用
class
会报错
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 挂载原型方法
call() {
console.log("my name is " + this.name);
};
// 挂载静态方法
static line() {
console.log("--------------------------------");
};
}
const xinxin = new Student('xinxin', 20);
const acall = new xinxin.call();
console.log(acall); // xinxin.call is not a constructor
function
依然会获得一个空对象
// 写一个构造函数
function Student(name, age) {
this.name = name;
this.age = age;
}
// 挂载原型方法
Student.prototype.call = function () {
console.log("my name is " + this.name);
};
// 挂载静态方法
Student.line = function () {
console.log("--------------------------------");
};
const xinxin = new Student('xinxin', 20);
const acall = new xinxin.call();
console.log(acall);
// my name is undefined
// {}
将class一比一转换为function写法
看到以上它们之间是有许多区别的,class多做了许多处理,所以肯定不是简单的用function写一个就和class写的效果一样,我们还需要进行其他的操作
class原型代码还是以上面写的为例
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 挂载原型方法
call() {
console.log("my name is " + this.name);
};
// 挂载静态方法
static line() {
console.log("--------------------------------");
};
}
分析:
- class是在严格模式下的,所以我们也要开启严格模式
- 要进行类型检查,检查是否为自己的实例对象
- class原型方法是不能被遍历的,所以要对原型属性的属性进行修改
- 最后就是将它们组合到一起
// 开启严格模式
'use strict';
// 类型检查(实例,构造函数)
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
// 对属性值进行修改(目标对象,属性修改数组)
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
// 默认不可枚举
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor)
descriptor.writable = true;
// 修改属性配置
Object.defineProperty(target, descriptor.key, descriptor);
}
}
// 最后进行组合(构造函数,原型方法,静态方法)
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps)
_defineProperties(Constructor.prototype, protoProps);
if (staticProps)
_defineProperties(Constructor, staticProps);
return Constructor;
}
// 创建Student类
var Student = /*#__PURE__*/function (name, age) {
// 构造器
function Student(name, price) {
// 进行验证
_classCallCheck(this, Student);
// 添加实例属性
this.name = name;
this.price = price;
}
// 添加实例方法和静态方法
_createClass(Student, [{
key: 'call',
value: function () {
console.log("my name is ".concat(this.name));
}
}], [{
key: 'line',
value: function () {
console.log('-------------------------------');
}
}]);
return Student;
}();
const xinxin = new Student('xinxin', 20);
xinxin.call(); // my name is xinxin
Student.line(); // -------------------------------
在JavaScript中,/#PURE/注释是一个特殊的注释,它告诉代码压缩工具如terser,标记的函数调用是“纯净的”,这意味着如果函数调用结果未被使用,它可以安全地删除
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
以上代码借鉴了Babel 的代码转义