类
定义类的方法
- 工厂方法
function creatPerson(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayName = function() {
window.alert(this.name);
};
return obj;
}
- 构造函数方法
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function() {
window.alert(this.name);
};
}
- 原型方法
function Person() {
}
Person.prototype = {
constructor : Person,
name : "Ning",
age : "23",
sayName : function() {
window.alert(this.name);
}
};
- 使用构造函数和原型方法(使用最广)
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor : Person,
sayName : function() {
window.alert(this.name);
}
};
构造函数
构造函数定义
定义:构造函数就是用new关键字创建对象时调用的函数
- 函数名首字母大写,用来区分普通函数
- 通过this来给对象添加属性和方法
- 使用new生成实例对象
使用构造函数的场景
在使用对象字面量创建一系列同一类型的对象时,这些对象可能具有一些相似的特征(属性)和行为(方法),此时会产生很多重复的代码,而使用构造函数就可以实现代码复用。
完整的构造函数的例子
// 创建一个构造函数
// 首字母大写
function Person(name){
this.name = name // 自定义属性
this.sayName = function(){ // 自定义方法
return this.name
}
}
// 实例化一个对象
var child = new Person('tom')
console.log(child) // [object]
console.log(child.name) // tom
console.log(child.constructor === Person) // true
console.log(child instanceof Person) // true,
constructor的作用是返回一个函数对象,该函数由数组对象的原始创建。
instanceof的作用检测左侧的__proto__原型链上,是否存在右侧的prototype原型
构造函数执行的过程
- 当以new关键字调用时,会创建一个新的内存空间,标记为XXX的实例
- 函数体内部的
this指向该内存 - 执行函数体内的代码,给this添加属性,就相当于给实例添加属性
- 默认返回this
构造函数与普通函数的区别
- 有new与无new的差别
- 构造函数也是一个普通函数,创建方式和普通函数一样,但构造函数习惯上首字母大写
- 调用方式不一样。作用也不一样(构造函数用来新建实例对象)
- 普通函数的调用方式:直接调用 person();
- 构造函数的调用方式:需要使用new关键字来调用 new Person();
- 构造函数的函数名与类名相同:Person( ) 这个构造函数,Person 既是函数名,也是这个对象的类名
- 内部用this 来构造属性和方法
- 用instanceof 可以检查一个对象是否是一个类的实例,是则返回true。
构造函数的优缺点
- 优点就是能够通过instanceof识别对象。
- 缺点是每次实例化一个对象,都会把属性和方法复制一遍。
var child1 = new Person("tom")
var child2 = new Person("kat")
console.log(child1.sayName === child2.sayName) // false
解决方案:通过原型(prototype)对象,把方法写在构造函数的原型对象上。
function Person (name){
this.name = name
}
Person.prototype.sayName = function(){
return this.name
}
var child1 = new Person("tom")
var child2 = new Person("kat")
console.log(child1.sayName === child2.sayName) // true
多个方法时,直接使用一个对象字面形式替换原型对象
Person.prototype = {
constructor :Person,
sayName:function(){
return this.name;
},
sayAge:function(){
return this.age
}
}
继承
父类有若干属性和方法,子类也有若干属性和方法,让多个子类拥有父类的属性和方法且相互之间不产生影响,这就是继承。
构造函数实现继承
原理:通过call实现的继承本质是改变了this指向,让父类里面的this指到子类的上下文,这样在父类里面通过this设置的属性或者方法会被写到子类上面。
function fun() {
this.name = 'fun'
}
fun.prototype.myLog = function() {
console.log(1)
}
function obj() {
fun.call(this)
this.type = 'child'
}
var O = new obj
console.log(O.myLog) // undefined
缺点:只能继承父类构造函数上的属性和方法,不能继承父类原型上的属性和方法。
通过原型链实现继承
原理:利用原型链向上查找的机制实现继承,给 obj.prototype 赋值为父类的一个实例,当把obj作为构造函数在它的实例O1上查找属性时查找顺序依次是 O1本身 -> obj.prototype(fun实例)-> fun.prototype 这样既能继承父类构造函数上的属性。也能继承父类原型上的属性。
function fun() {
this.name = 'fun'
this.arr = [1, 2, 3]
}
fun.prototype.myLog = function() { console.log(1) }
function obj(type) {
this.type = type
}
obj.prototype = new fun()
var O1 = new obj('o1')
var O2 = new obj('o2')
O1.name = 'is O1'
O1.arr.push('123')
console.log(O1.myLog) // 可以继承原型上的属性和方法
console.log(O2.name) // fun
console.log(O2.arr) // [1, 2, 3, '123']
构造函数+原型链 实现继承
原理:通过fun.call(this)改变上下文this指向,父类构造函数上的属性和方法设置到了子类上,相互独立避免影响;通过 obj.prototype = new fun() 实现了继承父类原型上的属性和方法。
function fun() {
this.name = 'fun'
this.arr = [1, 2, 3]
}
fun.prototype.myLog = function() { console.log(1) }
function obj () {
fun.call(this)
this.type = 'obj'
}
obj.prototype = fun.prototype
var O1 = new fun()
var O2 = new obj()
O1 instanceof obj // true
O2 instanceof obj // true
(new fun()).__proto__.constructor // 父类函数
(new obj()).__proto__.constructor // 父类函数
Object.create 实现继承
原理:通过create函数创建中间对象,把两个对象区分开,因为通过create创建的对象,原型就是create函数的参数。
function fun() {
this.name = 'fun'
this.arr = [1, 2, 3]
}
fun.prototype.myLog = function() {
console.log(1)
}
function obj() {
fun.call(this)
this.type = 'obj'
}
obj.prototype = Object.create(fun.prototype)
obj.prototype.constructor = obj
var O1 = new fun()
var O2 = new obj()
O1 instanceof obj // false
O2 instanceof obj // true
(new fun()).__proto__.constructor // 父类函数 fun()
(new obj()).__proto__.constructor // 子类函数 obj()
class
class 为构造函数的语法糖,即 class 的本质是 构造函数。class的继承 extends 本质 为构造函数的原型链的继承。
class 定义一个对象
class Student {
constructor(name) { // 构造函数
this.teacher = '王老师'
this.name = name
}
hello() { // 定义在原型对象上的函数
console.log(`我是${this.name},我的老师是${this.teacher}。`)
}
}
var xiaoming = new Student('小明')
var xiaohong = new Student('小红')
通过class定义的类需要实例化出对象的时候也需要new,这和前面说的对象都是new出来的相对应,区别在于通过class关键字定义类代码更简洁,避免了挂载prototype这种分散的代码。
class 继承
class Base {
constructor(name) {
this.name = name
this.school = 'xx大学'
this.course = ['语文', '数学']
this.teacher = '王老师'
}
modifyTeacher(tName) {
this.teacher = tName
}
}
class Student extends Base {
constructor(name) {
super(name)
this.time = new Date()
}
addCourse(course) {
this.course.push(course)
}
}
var xiaoming = new Student('小明')
var xiaohong = new Student('小红')
extends:extends关节字用来继承一个父类,子类拥有父类的属性和方法。(extends表示原型链对象来自Base)。 super():super用来调用父类的构造函数,否则父类的name属性无法正常初始化。