1-4-5-js-核心语法:类(ES6新增)(js class和构造函数的区别?)
一、js class和构造函数的定义
JavaScript是一种面向对象编程语言,它有两种创建类的方式,jsclass和构造函数。jsclass是一种ES6引入的语法,用来定义一个类,构造函数则是JavaScript早期的一种方式来创建类。
class MyClass{
constructor(a, b){
this.a = a
this.b = b
}
getValue(){
return `a is ${this.a}, b is ${this.b}`
}
}
let useMyClass = new MyClass(1,2)
useMyClass.getValue()
function MyClass(a, b) {
this.a = a
this.b = b
}
MyClass.prototype.getValue = function () {
return `a is ${this.a}, b is ${this.b}`
}
let useMyClass = new MyClass(1,2)
useMyClass.getValue()
// 注意:MyClass构造函数的名字,最好是首字母大写,这是一个好习惯,
// 这个构造函数只是一个普通函数,但是你创建的时候使用new 就是一个构造函数了,会返回一个object,
// 这个this就是指的new的时候,返回的对象,谁new的,这个this就是谁,
// 给对象添加属性,和普通的对象是一样的方法
myFather.nationality = "English";
// 给对象添加方法,和普通的对象是一样的方法
myFather.name = function () {
return this.firstName + " " + this.lastName;
};
// 给构造函数添加属性和方法,不能通过这种点的方式添加,必须要在构造函数内部添加,
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
this.nationality = "English";
}
二、jsclass和构造函数的继承
jsclass的继承使用extends
关键字来实现,子类调用父类的构造函数使用super
关键字。构造函数的继承需要使用call
或apply
来调用父类的构造函数。
class Student extends Person {
constructor(name, age, school) {
super(name, age);
this.school = school;
}
}
function Student(name, age, school) {
Person.call(this, name, age);
this.school = school;
}
三、jsclass和构造函数的原型
jsclass和构造函数的原型都可以添加方法和属性。jsclass使用prototype
关键字,构造函数使用prototype
属性。
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
}
四、jsclass和构造函数的静态方法
jsclass可以使用static
关键字来定义静态方法,静态方法可以直接通过类名调用,而不需要实例化。构造函数可以直接在函数对象上定义静态方法。
class Utils {
static add(a, b) {
return a + b;
}
}
console.log(Utils.add(1, 2)); // output: 3
function Utils() {}
Utils.add = function(a, b) {
return a + b;
}
console.log(Utils.add(1, 2)); // output: 3
五、jsclass和构造函数的实例化
jsclass需要使用new
关键字来实例化一个对象,而构造函数也可以使用new
关键字或者直接调用函数来实例化一个对象。
let p = new Person('Tom', 18);
p.sayHello(); // output: Hello, my name is Tom, I'm 18 years old.
let p = Person('Tom', 18);
console.log(p); // output: undefined
let p1 = new Person('Tom', 18);
console.log(p1); // output: Person { name: 'Tom', age: 18 }
new的问题
如果是类的方法,那只能用new,没有new的话报错
而构造函数可以,因为它本来就是一个函数,返回值为undefined,想要返回值,就得手动加上
function Man(name, age) {
this.name = name
this.age = age
return '我是没有new也行的返回值'
}
console.log(Man('小白', 19));
六、变量提升问题
console.log(Man);
function Man(name, age) {
this.name = name
this.age = age
}
是可以打印的
函数提升是 JavaScript 将声明移动到当前作用域顶端的默认行为。
但是用类的方法,就有暂时性死区了
console.log(Man);
class Man {
constructor(name, age) {
this.name = name
this.age = age
}
}
七,小结
通过以上对比,我们可以发现jsclass和构造函数都可以用来创建类,但是jsclass是ES6引入的面向对象编程语法,更加易读易写。同时,jsclass也拥有比构造函数更多的语法糖和功能支持,这使得程序员可以更快速地编写出复杂的程序。
原型对象,原型链
每一个函数,创建的时候,都会往属性里面添加一个属性prototype,
这个属性是对应一个对象,这个对象就是我们所谓的原型对象,
所以构造函数本身也会有一个原型对象,
使用构造函数创建对象的时候,每一个对象都有一个隐含的属性都是指向这个构造函数的原型对象的,
可以通过__proto__来访问这个对象,‘
function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
var myFather = new Person("Bill", "Gates", 62, "blue");
var myMother = new Person("Steve", "Jobs", 56, "green");
myFather.__proto__ == Person.prototype // 这个结果就是true
myMother.__proto__ == Person.prototype // 这个结果就是true
由于有这个性质,所以公共的方法和属性,可以放到原型对象里面,
// 往原型对象里面添加属性
Person.prototype.nationality = "English";
// 往原型对象里面添加方法
Person.prototype.name = function() {
return this.firstName + " " + this.lastName;
};
所以这个时候,对象调用属性和方法的时候,优先在自己的方法属性里面找,找不到就会去原型对象里面找,
类似父类的概念,这个地方体现了继承的思想,
解决了一个问题,
构造函数创建对象的方式,如果把方法放到构造函数,就会导致每次创建一个对象就会创建一个方法,创建1万个对象就是1万个方法,很浪费内存,
如果把这个方法写到全局变量里面,又会污染全局变量,如果别人使用了和你一样的变量名,就会导致你的方法被覆盖,也不可取,
而把这个公共的方法,放到原型对象里面,就完美的解决了这个问题,
function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
var myFather = new Person("Bill", "Gates", 62, "blue");
Person.prototype.nationality = "English";
"firstName" in myFather // 使用in,可以判断对象有没有这个属性,如果对象中没有,原型里有,也会是返回true,
myFather.hasOwnProperty("nationality") //使用hasOwnProperty,这个是判断这个是否是自己对象里面的属性,
注意:hasOwnProperty这个方法我本身没有定义这个方法,那是哪里来的?
往上找,是构造函数的原型对象里面的吗?也没有,
再往上找,构造函数的原型对象的原型对象,是object的原型对象,是这里面的方法,这个就是原型链的调用,
这样就到头了,
mc.proto.proto..hasOwnProperty("hasOwnProperty") // 这个就是true了,
toString方法揭秘
function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
var myFather = new Person("Bill", "Gates", 62, "blue");
console.log(myFather.toString()) //打印出 [object Object]
myFather.proto.proto.hasOwnProperty("toString")
// 这个tostring方法是哪里定义的,就是原型对象的原型对象里面定义的,
我们可以把这个改掉,
myFather.toString = function(){console.log("abc")}
console.log(myFather.toString()) // 这样覆盖了toString原来的方法之后,这个就是打印abc了