一、JavaScript常用的原型继承方式
- 原型链继承
2,构造函数继承(对象冒充继承)
3,组合继承(原型链继承+构造函数继承)
原型链继承
function Show(){
this.name="run";
}
function Run(){
this.age="20"; //Run继承了Show,通过原型,形成链条
}
Run.prototype=new Show();
var show=new Run();
alert(show.name)//结果:run
构造函数继承(对象冒充继承)
为了解决引用共享和超类型无法传参的问题,我们采用一种叫借用构造函数的技术,或
者成为对象冒充(伪造对象、经典继承)的技术来解决这两种问题
function Box(age){
this.name=['Lee','Jack','Hello']
this.age=age;
}
function Desk(age){
Box.call(this,age);
}
var desk = new Desk(200);
alert(desk.age);//200
alert(desk.name);//['Lee','Jack','Hello']
desk.name.push('AAA'); //添加的新数据,只给 desk
alert(desk.name)//['Lee','Jack','Hello','AAA']
组合继承(原型链继承+构造函数继承)
借用构造函数虽然解决了刚才两种问题,但没有原型,复用则无从谈起。所以,我们需要原型链+借用构造函数的模式,这种模式成为组合继承。
function Box(age) {
this.name = ['Lee', 'Jack', 'Hello']
this.age = age;
}
Box.prototype.run = function () {
return this.name + this.age;
};
function Desk(age) {
Box.call(this, age); //对象冒充
}
Desk.prototype = new Box(); //原型链继承
var desk = new Desk(100);
alert(desk.run());
二、class类的使用
ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。类和模块内部使用的是严格模式
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
类的定义需要constructor方法这是构造方法,this指向实例对象,定义方法时方法之间不需要使用逗号分隔,加了会报错
类的数据类型是函数,它的指向是构造函数,所以在使用的时候和构造函数相同需要new关键字
class Bar {
doStuff() {
console.log('stuff');
}
}
var b = new Bar();
b.doStuff() // "stuff"
es6中的类也有prototype属性
class Point {
constructor() {}
toString() {}
toValue() {}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
let b = new Point();
//调用相同以下
b.constructor === B.prototype.constructor // true
Object.assign可以使prototype同时绑定多个方法
class Point {
constructor(){}
}
Object.assign(Point.prototype, {
toString(){},
toValue(){}
});
prototype对象的constructor属性,直接指向“类”的本身,这与 ES5 的行为是一致的。
Point.prototype.constructor === Point // true
类的内部所有定义的方法,都是不被枚举的
class Point {
constructor(x, y) {}
toString() {}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
上面代码中,toString方法是Point类内部定义的方法,它是不可枚举的。这一点与 ES5 的行为不一致。
var Point = function (x, y) {}
Point.prototype.toString = function() {};
Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
上面代码采用 ES5 的写法,toString方法就是可枚举的。
类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行
生成类的实例对象的写法,与 ES5完全一样,也是使用new命令。前面说过,如果忘记加上new,像函数那样调用Class,将会报错,同时class的原型书写格式也和es5相差不大
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = Point(2, 3);//报错
var point = new Point(2, 3);
point.toString() // (2, 3)
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
采用 Class 表达式,可以写出立即执行的 Class。
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('张三')
Class 的取值函数(getter)和存值函数(setter)
class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get html() {
return this.element.innerHTML;
}
set html(value) {
this.element.innerHTML = value;
}
}
var descriptor = Object.getOwnPropertyDescriptor(
CustomHTMLElement.prototype, "html"
);
"get" in descriptor // true
"set" in descriptor // true
上面代码中,存值函数和取值函数是定义在html属性的描述对象上面,这与 ES5 完全一致。
class静态方法static关键字不会被实例继承但是会被子类继承
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()// TypeError: foo.classMethod is not a function
class Bar extends Foo {}
Bar.classMethod() // 'hello'
//也可以这么使用
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', too';
}
}
Bar.classMethod() // "hello, too"
new.target属性new是从构造函数生成实例的命令。ES6为new命令引入了一个new.target属性,该属性一般用在在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。
私有方法是常见需求,但 ES6 不提供,只能通过变通方法模拟实现。
在命名上加以区别
class Widget {
// 公有方法
foo (baz) {
this._bar(baz);
}
// 私有方法
_bar(baz) {
return this.snaf = baz;
}
}
与私有方法一样,ES6 不支持私有属性。目前,有一个提案,为class加了私有属性。方法是在属性名之前,使用#表示。
class Point {
#x;
constructor(x = 0) {
#x = +x; // 写成 this.#x 亦可
}
get x() { return #x }
set x(value) { #x = +value }
}