传统的javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。这样的写法相对于其它传统面向对象语言来讲,很有一种独树一帜的感脚!非常容易让人困惑!
如果要生成一个对象实例,需要先定义一个构造函数,然后通过new操作符来完成。构造函数示例:
//函数名和实例化构造名相同且大写(非强制,但这么写有助于区分构造函数和普通函数)
function Person(name,age) {
this.name = name;
this.age=age;
}
Person.prototype.say = function(){
return "我的名字叫${this.name}今年${this.age}岁了";
}
var obj=new Person("shu",27);//通过构造函数创建对象,必须使用new 运算符
console.log(obj.say());//我的名字叫shu今年27岁了
构造函数生成实例的执行过程:
1.当使用了构造函数,并且new 构造函数(),后台会隐式执行new Object()创建对象;
2.将构造函数的作用域给新对象,(即new Object()创建出的对象),而函数体内的this就代表new Object()出来的对象。
3.执行构造函数的代码。
4.返回新对象(后台直接返回);
ES6引入了Class(类)这个概念,通过class关键字可以定义类。该关键字的出现使得其在对象写法上更加清晰,更像是一种面向对象的语言。
class Person{//定义了一个名字为Person的类
constructor(name,age){//constructor是一个构造方法,用来接收参数
this.name = name;//this代表的是实例对象
this.age=age;
}
say(){//这是一个类的方法,注意千万不要加上function
return "我的名字叫" + this.name+"今年"+this.age+"岁了";
}
}
var obj=new Person("shu",27);
console.log(obj.say());//我的名字叫shu今年27岁了
console.log(typeof Person); //function
console.log(Person === Person.prototype.constructor); //true
从这两段代码可以看书,其实Person类实质上就是一个函数。类自身指向的就是构造函数。所以可以认为ES6中的类其实就是构造函数的另外一种写法.
类的所有方法都定义在类的prototype属性上
Person.prototype.addFn=function(){
return "我是通过prototype新增加的方法,名字叫addFn";
}
var obj=new Person("李大师",30);
console.log(obj.addFn());
//我是通过prototype新增加的方法,名字叫addFn
除此之外,我们还可以通过Object.assign方法来为对象动态的增加方法
Object.assign(Person.prototype, {
getName : function() {
return this.name
},
getAge : function() {
return this.age
}
})
var obj=new Person("李大师",30);
console.log(obj.getName());//李大师
console.log(obj.getAge());//30
解释一下constructor
constructor 方法是类的构造函数的默认方法,通过new 命令生成实例对象时,自动调用该方法
class Desk{
constructor(){
this.day="今天风很大";
return this
}
}
class Box{
constructor(){
return new Desk();// 这里没有用this哦,直接返回一个全新的对象
}
}
var obj=new Box();
console.log(obj.day);//今天风很大
constructor 中定义的属性或方法与 constructor外定义的属性或方法有什么区别呢?
class Person {
constructor() {
this.name = '李大师';
this.age = 30;
this.showName = function() {
console.log(this.name)
}
this.showweight = function(){
console.log(this.weight)
}
}
height = 175;
weight=70; sex = 'man'; salary= 500000;
showAge() {
console.log(this.age)
}
showheight(){
console.log(this.height)
} showSex = function(){ console.log(this.sex) } showSalary = ()=>{ console.log(this.salary) }
}
const person = new Person();
console.log(person.hasOwnProperty('name'));
// true
console.log(person.hasOwnProperty('name'));
// true
console.log(person.hasOwnProperty('height'));
// true
console.log(person.hasOwnProperty('weight'));
// true
console.log(person.hasOwnProperty('showName'));
// true
console.log(person.hasOwnProperty('showAge'));
// false
console.log(person.hasOwnProperty('showheight'));
// false
console.log(person.hasOwnProperty('showweight'));
// true
console.log(person.hasOwnProperty('showSex'));
// true
console.log(person.hasOwnProperty('showSalary'));
// true
从上面的代码我们可以看出
(1)无论是在constructor中,通过this.name = name 这种方式所绑定的属性和方法,最终会在实例对象中体现出来
(2)在constructor外部,通过showSex = function(){} 或者 showSalary = ()=>{} 这种方式绑定的方法,也会在实例对象中体现出来,有图为证
(3)在constructor外部,通过showAge(){} 这种方式绑定的方法,实际上是将方法绑定在了Person.prototype上面,有图为证
综上所述:
class中,所有的属性,无论是否在contructor中指定,都会绑定到class的实例对象上;class中,通过showAge(){}这种方式绑定的方法,会默认的绑定到class的prototype属性上面去,并不会在class的实例对象上体现,而通过constructor中的this.showName = function(){} 或者constructor外部的 showSex = function(){} 这样的形式绑定的方法,将会绑定到class的实例对象上面去,在prototype上面并不会有体现。
根据上面的原则,我们可以发现,如果要定义一个class,那么对于那些需要在后代中继承的方法,可以通过showAge(){} 这样的形式,绑定到class的prototype属性上去,如果不需要在后代中继承,只需要在实例中使用的方法,就可以通过在constructor中定义,或者通过showSex = ()=>{} 这样的方式,绑定到它的实例对象中区。
在ES6中,使用extends关键字来实现继承
class Animal {
constructor(){}
name = 'animal';
age = 3;
showName() {
console.log( 'animalName =' + this.name)
}
showAge = ()=>{
console.log('animalAge = ' + this.age)
}
}
class Dog extends Animal {
constructor(){
super()
}
name='dog';
age=5;
fullname = 'Dog';
sex='male';
showSex() {
console.log(this.sex)
}
showName =()=> {
console.log('name' + this.name)
}
showFullName(){
console.log(this.name + this.fullname)
}
}
const dog = new Dog();
dog.showAge();
//animalAge = 5
dog.showName();
// namedog
dog.showSex();
// male
delete dog.showName
dog.showName()
// animalName =dog
我们可以发现,如果一个class的方法并不是定义在prototype方法上,而是像通过赋值一样showName = ()=>{} 这种方式,那么如果delete 了 实例对象上的属性,这个属性就会消失,再执行这个方法,就会向上访问它的原型 dog.__proto__ 也就是Dog.prototype上去查找,但是Dog.prototype上面也是没有showName属性的,那么它只好向上查找,因为Dog是Animal的子类,Dog.prototype.__proto__ === Animal.prototype, Animal.prototype上面是有showName属性的,那么可以得到最后一个结论,delete dog.showName之后 ,dog.showName() 会输出 animaleName =dog