首先,我们需要知道class是用来干什么的?class是用来声明类的。每一种类都有自己的属性和行为。比如说,汽车,都能跑、有四个轮子等等。我有一辆车,那么我的车就是汽车这个类的一个实例对象。再比如说人类,人都有姓名、年龄,会说话,我是一个人,那我就是人类的一个对象实例。class的作用和构造函数一样都是用于创建对象的
那么在js中,我们都有哪些方法来创建对象呢?
第一种,使用字面量创建对象
let person = {
name: 'chen',
age: 29,
say: function(){
return 'I am a man';
}
};
console.log(person.say());
使用字面量创建对象,优点在于结构清晰明了,代码量少,但是如果现在的需求是有张三、李四、王五三个人,每个人都有say函数,那么按照字面量的方式就需要将person复制出3份才行。必然会有很多的重复代码。
所以,我们还可以通过构造函数创建对象,使用new操作符创建对象,可以创建很多结构类似的对象
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function() {
return 'I am a man';
}
}
let person = new Person(‘chen’, 29);
当我们需要创建多个人的时候,只需要使用new关键字创建多个对象就可以了。
实际上构造器就是一个普通的函数。当使用new 操作符 来作用这个函数时,它就可以被称为构造函数
所以关键在于new操作符。
那么大家还记得new关键字的作用吗?这也是前端面试经常会问到的问题
如果不记得了,没关系,我们自己来写一个new函数来模拟new关键字的作用,
当我们使用new操作符创建对象的时候,new关键字会帮我们做一下的操作:
创建一个空的简单JavaScript对象(即{});
将步骤1中创建的对象的__proto__属性绑定到构造函数的prototype属性上 ;
将步骤1新创建的对象作为this的上下文 ;
如果该函数没有返回对象,则返回this。
我们可以自己来写一个new函数,同样可以达到new操作符做到的事情
function newFunc(Constructor, …args) {
// 1.声明变量obj,赋值为空对象
let obj = {};
// 继承构造函数的原型
obj.__proto__ = Constructor.prototype;
// 看到这里的同学可能又会对__proto__和prototype产生疑惑,它们有什么不同吗?
/*
简单的说,每个对象都有一个__proto__属性,并且指向它的prototype原型对象
每个函数,也只有函数才会有prototype属性
看看上面的let obj = {},当我调用obj.toString()函数的时候,会报错吗,当然不会报错,可是我并没有定义toString函数啊,为什么没有报错呢?我们通过字面量创建的对象,我们可以认为就是使用new Object()来创建的,实际上Object就是一个函数,函数都会有prototype属性的,所以obj对象的__proto__属性会链接到Object函数的prototype属性上,当我们试图访问一个对象的属性时,它不仅仅在该对象上查找,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾也就是null,这就是js中的原型链的概念了,实际上toString函数就是在Object.prototype属性对象上找到的。*/
// 3.将obj作为Constructor函数的this
let result = Func.apply(obj, args);
// 这里的apply就是用于改变Constructor函数的this的
// 如果没有return则返回obj
return result || obj;
}
let person = newFunc(Person, 'chen', 29);
现在我们再回到这个Person构造函数中,实际上,这个构造函数是有一点问题的,因为我们每new一个对象时,都会创建一个say函数,这样无疑会增加很多不必要的内存开销。
所以真实情况下,我们都会将函数属性添加到构造函数的原型上去,而不是在构造函数中声明。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function() {
return 'I am a man';
}
Let person = new Person();
Person.say();
现在,不管我们调用多少次new,say函数永远只会创建一个,如果一个构造函数有很多个函数属性,那么只需要在这个构造函数的prototype属性上添加函数就可以了。
我们可以看看这个写法,实际上不够优雅,有多个函数属性的时候,需要多次写prototype,而且一个构造函数,也是写的七零八落的。
所以ES6引入了Class(类)这个概念,通过class关键字可以定义类
如果要使用class实现刚刚的构造函数,要如何写呢,我们先使用class来实现,等会再来一一讲解。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
say() {
return 'I am a man';
}
}
let person = new Person(‘chen', 29);
Person类中的constructor是一个构造方法,用来接收参数,在constructor之外的函数就是添加在在Person的原型上的。需要注意的是,使用class创建对象是,一定要加new操作符的,不然会报错,而使用构造函数没有加new操作符也不会报错。
但是,实际上class实际上就是 JavaScript 现有的基于原型的继承的语法糖,只是在构造函数的基础上进行了封装。之所以叫语法糖,是因为没有带来新的语言特性,只是基于之前的功能进行新的封装实现,让代码写起来更加简洁。就像加了糖的牛奶,喝起来味道更好。
像我们之前学习的对象解构、箭头函数等等都是语法糖。
接下来,我们来看看class基本语法
class关键字
类名,如果不写就是匿名类
一个类的类体是一对花括号 {} 中的部分。
constructor方法
是一个特殊的方法,这个方法用于创建和初始化一个由class创建的对象。在一个类中只能有一个构造函数。
有一点需要注意的是class的所有函数都是以严格模式下执行的,所以当this找不到时,this就是undefined
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
say() {
return this.name;
}
}
let person = new Person(‘chen', 29);
Let say = person.say;
say();
静态类型/方法
static关键字用来定义一个类的一个静态方法。调用静态方法不需要实力话该类,直接通过类名调用,但不能通过一个类实例调用静态方法。
class Person {
static address = '地球'
constructor(name, age) {
this.name = name;
this.age = age;
}
static say() {
return 'I am a man';
}
}
热门评论
总结的很好