一、构造函数
首先,构造函数其实就是JavaScript程序中定义好的函数,我们可以直接使用。构造函数实际也是一种函数,它是专门用于生成定义对象,通过构造函数生成的对象,称为实例化对象。
构造函数分为两种,一种是JavaScript程序定义好的构造函数,我们成为称为内置构造函数,另外一种是程序员自己定义的构造函数,称为自定义构造函数。
构造函数虽然也是一种函数,但是和普通函数是区别的:
1、构造函数一定要和关键词new一起使用,new关键词会自动的给构造函数定义一个对象,并且返回这个对象,我们只要对这个对象设定属性,设定方法就可以使用。
2、构造函数为了和其他函数区别,语法规范规定构造函数的函数名称,第一个字母必须大写,使用大驼峰命名法。
3、构造函数给对象定义属性和方法的语法,与一般函数不同
//自定义构造函数
function Person(name,sex,age,addr){
// 定义属性
this.name = name;
this.sex = sex;
this.age = age;
this.addr = addr;
// 定义方法
this.fun = function(){
console.log(this.name,this.sex,this.age,this.addr);
}
}
// 生成实例化对象
const person = new Person('终结者','男',28,'杭州');
console.log(person); //输出实例化对象
普通构造函数实现继承
//普通构造函数继承
//动物
function Animal() {
this.eat = function () {
console.log('animal eat')
}
}
//狗
function Dog() {
this.bark = function () {
console.log('dog bark')
}
}
//绑定原型,实现继承
Dog.prototype = new Animal()
//哈士奇
var hashiqi = new Dog()
二、class
ES6构造函数语法:ES6与ES5构造函数语法对比,其功能作用完全相同,只是语法不同。ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
class Perosn{// ES6class方法定义构造函数
//constructor是构造器,定义实例化对象的属性和属性值, ()中的是参数
constructor (name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
//定义方法,虽然没声明,但是也是定义在构造函数中的prototype中
printAll(){
console.log(this.name,this.age,this.sex);
}
}
// 生成实例化对象
const test = new Perosn('终结者','男',28,'杭州');
console.log(person);//输出实例化对象
person.printAll(); //输出方法
上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5的构造函数Person,对应ES6的Person类的构造方法。
定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
Class实现继承
class Animal{
constructor(name){
this.name=name
}
eat(){
console.log(this.name +'eat');
}
}
class Dog extends Animal{
constructor(name){
super(name)//访问和调用Dog对象的父对象Animal上的函数。
this.name=name
// 在派生类中, 必须先调用 super() 才能使用 "this",忽略这个,将会导致一个引用错误。
}
bark(){
console.log(this.name+'bark');
}
}
const dog=new Dog("泰迪");
dog.eat();
dog.bark();
构造函数的prototype属性,在ES6的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。
ES6中的class语法就是ES5中构造函数的另一种写法,一种更高级的写法,
class语法的底层还是ES5中的构造函数,只是把构造函数进行了一次封装而已。
ES6出现的目的为了让我们的让对象原型的写法更加清晰、在语法上更加贴合面向对象的写法、更像面向对象编程让JavaScript更加的符合通用编程规范,即大部分语言对于类和实例的写法,class实现继承更加容易理解,更易于后端语言使用。
三、构造函数与Class的区别
类不存在变量提升(hoist),这一点与 ES5 完全不同。
// ES5
var bar = new Bar(); // 可行
function Bar() {
this.bar = 42;
}
//ES6
const foo = new Foo(); // Uncaught ReferenceError
class Foo {
constructor() {
this.foo = 42;
}
}
类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。
// ES5
function Bar() {
// 引用一个未声明的变量
baz = 42; // it's ok
}
var bar = new Bar();
// ES6
class Foo {
constructor(){
// 引入一个未声明的变量
fol = 42;
// Uncaught ReferenceError: fol is not defined
}
}
let foo = new Foo();
ES6 中的 class,它的方法(包括静态方法和实例方法)默认是不可枚举的,而构造函数默认是可枚举的。细想一下,这其实是个优化,让你在遍历时候,不需要再判断 hasOwnProperty 了
// ES5
function Bar() {}
Bar.answer = function () {};
Bar.prototype.print = function () {};
console.log(Object.keys(Bar));// ["answer"]
console.log(Object.keys(Bar.prototype))// ["print"]
// ES6
class Foo {
constructor(){}
static answer() {}
print(){}
}
console.log(Object.keys(Foo))// []
console.log(Object.keys(Foo.prototype));// []
class 必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
// ES5
function Bar(){ }
var bar = Bar();// it's ok;
// ES6
class Foo {
}
let foo = Foo();
// Uncaught TypeError: Class constructor Foo cannot be invoked without 'new'
// ES5
function Bar() {
Bar = 'Baz';
this.bar = 42;
}
var bar = new Bar();
console.log(bar);// Bar {bar: 42}
console.log(Bar);// 'Baz'
// ES6
class Foo {
constructor(){
this.foo = 42;
Foo = 'Fol'; // Uncaught TypeError: Assignment to constant variable.
}
}
let foo = new Foo();
Foo = 'Fol';// it's ok
一条是:子类的__proto__指向父类
另一条:子类prototype属性的__proto__属性指向父类的prototype属性.
ES6的子类可以通过__proto__属性找到父类,而ES5的子类通过__proto__找到Function.prototype
// ES5
function Super() {}
function Sub() {}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
var sub = new Sub();
console.log( Sub.__proto__ === Function.prototype);// true
// ES6
class Super{}
class Sub extends Super {}
let sub = new Sub();
console.log( Sub.__proto__ === Super);// true
ES5的继承是先建立子类实例对象this,再调用父类的构造函数修饰子类实例(Surper.apply(this))。
ES6的继承是先建立父类实例对象this,再调用子类的构造函数修饰this。即在子类的constructor方法中必须使用super(),之后才能使用this.
// ES5
function MyES5Array() {
Array.apply(this, arguments);
// 原生构造函数会忽略apply方法传入的this,
//即this无法绑定,先生成的子类实例,拿不到原生构造函数的内部属性。
}
MyES5Array.prototype = Object.create(Array.prototype, {
constructor: {
value: MyES5Array,
writable: true,
configurable: true,
enumerable: true
}
})
var arrayES5 = new MyES5Array();
arrayES5[0] = 3;
console.log(arrayES5.length);// 0
arrayES5.length = 0;
console.log(arrayES5[0]);// 3
// ES6
class arrayES6 extends Array {
constructor(...args){
super(...args);
}
}
let arrayes6 = new arrayES6();
arrayes6[0] = 3;
console.log(arrayes6.length);// 1
arrayes6.length = 0;
console.log(arrayes6[0]);// undefined
需要注意一点:
ES6的class语法无法执行预解析,是不能被提前调用;
ES5的function函数可以提前调用,但是只能调用属性不能调用方法。