这篇文章主要讲了JavaScript ES6之后有类的概念的面向对象的方法和ES5之前没有类的概念时的面向对象的方法
如有错误,欢迎前来指正
// 声明类
class 类名 {
// class body
}
// 实例化对象
var 对象名 = new 类名();
class Name {
constructor(uname) {
// 方法体
this.uname = uname;
}
}
var name = new Name('菜鸟小铭');
console.log(name.uname);
// 输出:菜鸟小铭
class Person {
say() {
console.log('hello');
}
}
class Father {
// 父类
}
class Son extends Father {
// 子类继承父类
}
class Father {
constructor(x,y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x+this.y);
}
}
class Son extends Father {
constructor(x,y) {
this.x = x;
this.y = y;
}
}
var son = new Son(1,2);
son.sum();
class Father {
constructor(x,y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x+this.y);
}
}
class Son extends Father {
constructor(x,y) {
super(x,y); // 调用了父类的构造函数
}
}
var son = new Son(1,2);
son.sum();
如果子类里的函数和父类有同名函数,子类的函数会覆盖父类的函数
super 调用父类函数 super.函数()
super()
调用的是父类的构造函数
在构造函数里,super 必须在子类 this 之前调用!
例如:
// 某一个子类的构造函数
constructor(x,y) {
// super 必须在子类 this 之前调用
super(x,y);
this.x = x;
this.y = y;
}
ES6 里类没有变量提升,所以要先定义类再实例化对象
类里的属性和方法一定要加 this
案例:现在有一个按钮,我们点击一下就输出一个字
<button>点击button>
<script>
class Father {
constructor(uname) {
this.uname = uname;
this.btn = document.querySelector('button');
// 这里的函数不能加括号,因为一加括号他就立即执行了
this.btn.onclick = this.sing;
}
sing() {
console.log('hello world');
}
}
var father = new Father('菜鸟小铭');
script>
这里面绑定事件的时候千万不要加括号,像这样this.btn.onclick = this.sing()
这样的化他会直接调用这个函数,而不是点击一下调用这个函数
this 指向问题
// 通过 new 来创建对象
var obj1 = new Object();
// 通过 字面量 创建对象
var obj2 = {};
// 通过构造函数创建对象
function Star(uname, age) {
// 变量
this.uname = uname;
this.age = age;
// 方法
this.sing = function() {
console.log('我会唱歌');
}
}
// 创建对象
var noobMing = new Star('菜鸟小铭', 18);
function Star(uname) {
// 实例成员
this.uname = uname;
}
// 静态成员
Star.age = 18;
构造方法的问题:浪费内存的问题,复杂数据类型 (例如函数) 他会另外开辟一个内存空间存相同的数据
为了解决这个问题,每个构造函数都会有一个自带的对象 prototype,我们可以把那些不变的方法直接定义在 prototype 上,所有的对象可以共享这些方法
代码示例:
function Star(uname, age) {
// 变量
this.uname = uname;
this.age = age;
}
// 原型对象里的方法
Star.prototype.sing = function() {
console.log('我会唱歌');
}
Star.prototype = {
sing: function() {
console.log('我会唱歌');
}
}
注意前后都是两个下划线
对象都会有这个属性,指向构造函数的 prototype 原型对象,所以我们可以使用构造函数里没有的方法 (有点像双向绑定)
__proto__ === prototype
非标准属性,在实际开发的时候不能使用这个属性
Object.prototype
__proto__
进入原型对象 (prototype) 上查找有没有这个成员,再没有的话就去 Object.prototype 里面去找,再找不到的话就返回 null (undefined)使用
Array.prototype
就可以查看里面的方法
Array.prototype.sum = function() {
var sum = 0;
for(var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
}
// 普通函数
function fn(x, y) {
console.log(this);
console.log(x + y);
}
var o = {
name: "菜鸟小铭"
};
// 1. call 调用函数
fn.call();
// 2. call 改变 this 的指向
fn.call(o, 1, 2);
// 输出 this 指向 o,第二行输出 3
function Father(uname, age) {
// this 指向 Father
this.uname = uname;
this.age = age;
}
function Son(uname, age) {
// 使 Son 继承 Father 的属性
Father.call(this, uname, age);
}
父类的方法是写在 prototype 里面的,光凭上面的调用函数是继承不过来父类的方法
同样不能让父原型对象直接等于子原型对象,这样我们再往子原型对象中添加方法时父原型对象也会添加一样的方法 (这个的原因和实参差不多,父原型对象的地址给了子原型对象,当子原型对象添加方法时,父原型对象因为使用的是同一个地址所以也会发生改变)
实际使用时让 Father实例对象 来当一个中间站,然后再赋值给 son 的原型对象
Son.prototype = new Father();
__proto__
,所以并不会影响到父类的原型对象Son.prototype.constructor = Son