算是东拼西凑的一篇文章吧,但是确实觉得js实现类和公有私有方法,也就是面向对象的一些东西是蛮重要的
基础知识
JavaScript中的类
JavaScript实际上是一种弱类型语言,与C++和Java等语言不同。因此,在JavaScript中,没有强调类(class)这一概念,但实际运用中,类还是很重要的,比如写一款游戏,如果我们不停地调用函数来完成创建角色,移动角色的话,那会是什么样的呢?可能会出现非常多的重复代码,因此我们需要一个类来统一这些代码。所谓的类,就是把程序中的代码分类,比如说游戏中的关于角色的代码算作一类,游戏背景算作一类,游戏特效又是一类。这样一来,我们对类进行操作,就不会使代码显得很凌乱,冗杂。虽然Js是弱类型语言,但是也提供了类这一概率。
定义Js中的类,实际上用的是function
,总所周知,这个语法其实是用来定义函数的。不同于定义函数的是,我们可以在function
中通过this.xxx
的方式来定义属性和方法。比如说:
function People () {
this.name = "Yorhom";
this.getName = function () {
return this.name
};
}
使用的时候使用new
:
var yorhom = new People();
// "Yorhom"
alert(yorhom.getName());
可以看到,这样就可以使用到我们定义的类和类中的方法了。
也许你会问this.xxx
只能定义公有属性和方法,那私有属性和方法怎么办呢?这个可以用到js闭包的知识来解决:
function People () {
this.name = "Yorhom";
var age = 16;
this.getName = function () {
return this.name
};
this.getAge = function () {
return age;
};
}
var yorhom = new People();
// undefined
alert(yorhom.age);
// 16
alert(yorhom.getAge());
可以看到,这里的age就是一个私有属性了。
JavaScript中的prototype
上面的代码美中不足的地方就是,如果一个类有很多方法,同时用到这个类的地方又有很多(也就是new
出来的对象有很多),那么用上面的代码就会出现内存占用过剩的问题。问题的根本原因在于,每次实例化一个对象,这个类就会执行构造器里的代码(以People类为例就是function People () {…}执行的代码),因此每当这个类被实例化的时候,这些方法和属性就会被拷贝到实例化出来的对象中。这样一来,就会造成“吃”内存的现象。
于是js中的prototype
就诞生了。prototype
的作用通常是给一个类添加一系列常量或者方法。 每当一个类被实例化之后,实例化出来的对象会自动获取类的prototype
中定义的方法和属性。只不过这里的获取类似于C++里面的引用,不会在内存里对这些方法和属性进行复制,而是指向这些方法和属性。示例:
function People () {
this.name = "Yorhom";
}
People.prototype.getName = function () {
return this.name;
};
var yorhom = new People();
// "Yorhom"
alert(yorhom.getName());
这种方法虽然可以节约内存,但是,美中不足的是,无法定义私有属性。
继承
版本1:来自书上的官方说法,老实说不太容易记住
一.类式继承
简介:将父类对象的实例赋值给子类的原型,则子类的原型可以访问父类原型上的属性和方法,以及父类构造函数中复制的属性和方法。
- //1.类式继承
- //声明父类
- function SuperClass() {
- this.superValue = true;
- }
- //为父类添加公有方法
- SuperClass.prototype.getSuperValue = function () {
- return this.superValue;
- };
- //声明子类
- function SubClass() {
- this.subValue = false;
- }
- //继承父类
- SubClass.prototype = new SuperClass();//将父类对象赋值给子类原型,子类原型可访问父类原型上的属性和方法--类式继承原理
- //子类添加公有方法
- SubClass.prototype.getSubValue = function() {
- return this.subValue;
- };
- //测试
- var instance = new SubClass();
- console.log(instance.getSuperValue());//t
- console.log(instance.getSubValue());//f
- //
- console.log(instance instanceof SuperClass);//t
- console.log(instance instanceof SubClass);//t
- console.log(SubClass instanceof SuperClass);//f
- console.log(SubClass.prototype instanceof SuperClass);//t
类式继承有两个缺点:
1.子类通过其原型prototype对父类实例化,继承了父类。但当父类中的共有属性是引用类型时,会在子类中被所有的实例共用,如此在一个子类实例中更改从父类中继承过来的公有属性时,会影响到其他子类。
- function SuperClass() {
- this.book = ['javascript','html','css'];
- }
- function SubClass() {}
- SubClass.prototype = new SuperClass();
- var ins1 = new SubClass();
- var ins2 = new SubClass();
- console.log(ins1.book);
- console.log(ins2.book);
- ins1.book.push("Node");
- console.log("after");
- //引用类型在子类中被所有实例共用
- console.log(ins1.book);//[ 'javascript', 'html', 'css', 'Node' ]
- console.log(ins2.book);//[ 'javascript', 'html', 'css', 'Node' ]
2.由于子类是通过原型prototype实例化父类实现继承的,所以在创建父类的时候,无法向父类传递参数,因而在实例化父类的时候无法对父类构造函数内的属性初始化。
二.构造函数式继承
简介:通过在子类的构造函数中执行一次父类的构造函数实现。
- //2.构造函数式继承
- //声明父类
- function SuperClass(id) {
- this.book = ['javascript','html','css'];//引用类型共有属性
- this.id = id;//值类型公有属性
- }
- //父类声明原型方法
- SuperClass.prototype.showBooks = function() {
- console.log(this.books);
- }
- //声明子类
- function SubClass(id) {
- //继承父类
- SuperClass.call(this,id);
- }
- //测试
- var ins1 = new SubClass(1);
- var ins2 = new SubClass(2);
- ins1.book.push("Node");
- console.log(ins1.id);//1
- console.log(ins1.book);//['javascript', 'html', 'css', 'Node']
- console.log(ins2.id);//2
- console.log(ins2.book);//['javascript', 'html', 'css']
- ins1.showBooks();//TypeError: ins1.showBooks is not a function
缺点:
这种类型的继承没有涉及原型prototype,所以父类的原型方法不会被子类继承。如想被子类继承就必须放在构造函数中,这样创造的每个实例都会单独拥有一份而不能共用,违背了代码复用原则。
三.组合式继承
简介:综合以上两种模式的优点,在子类原型上实例化父类,在子类构造函数中执行一遍父类的构造函数。这样融合了类式继承和构造式继承的优点,过滤了缺点。
- //3.组合式继承
- function SuperClass(name) {
- this.name = name;
- this.book = ['javascript','html','css'];
- }
- SuperClass.prototype.getName = function () {
- console.log(this.name);
- };
- function SubClass(name,time) {
- //构造函数式继承,继承父类name属性
- SuperClass.call(this,name);
- this.time = time;
- }
- //类式继承,子类原型继承
- SubClass.prototype = new SuperClass();
- //子类原型方法
- SubClass.prototype.getTime = function () {
- console.log(this.time);
- };
- //测试
- var ins1 = new SubClass('Node',2016);
- ins1.book.push("Node");
- console.log(ins1.book);
- ins1.getName();
- ins1.getTime();
- var ins2 = new SubClass('React',2015);
- console.log(ins2.book);
- ins2.getName();
- ins2.getTime();
父类的构造函数执行了两遍:一次在子类的构造函数中call方法执行一遍,一次在子类原型实例化父类的时候执行一遍。
四.原型式继承
简介:对类式继承的封装,过渡对象相当于子类。
- //4.原型式继承
- function inheritObject(o) {
- //声明过渡函数对象
- function F() {}
- //过渡对象的原型继承父类
- F.prototype = o;
- return new F();
- }
- //测试
- var book = {
- name : "javascript",
- book : ['js','css']
- };
- var newbook = inheritObject(book);
- newbook.name = "ajax";
- newbook.book.push("Node");
- var otherbook = inheritObject(book);
- otherbook.name = "xml";
- otherbook.book.push("React");
- console.log(newbook.name);//ajax
- console.log(newbook.book);//[ 'js', 'css', 'Node', 'React' ]
- console.log(otherbook.name);//xml
- console.log(otherbook.book);//[ 'js', 'css', 'Node', 'React' ]
- console.log(book.name);//javascript
- console.log(book.book);//[ 'js', 'css', 'Node', 'React' ]
和类式继承一样,父类对象的引用类型值被共用。
五.寄生式继承
简介:寄生式继承其实是对原型继承的第二次封装,并且在第二次封装的过程中对继承的对象进行了拓展。
- //5.寄生式继承
- function inheritObject(o) {
- //声明过渡函数对象
- function F() {}
- //过渡对象的原型继承父类
- F.prototype = o;
- return new F();
- }
- //声明基对象
- var book = {
- name : "javascript",
- book : ['js','css']
- };
- function createBook(obj) {
- //通过原型继承方式创建新对象
- var o = new inheritObject(obj);
- //拓展新对象
- o.getName = function() {
- console.log(name);
- }
- //返回拓展后的新对象
- return o;
- }
- var newbook = createBook(book);
- newbook.name = "ajax";
- newbook.book.push("Node");
- var otherbook = createBook(book);
- otherbook.name = "xml";
- otherbook.book.push("React");
- console.log(newbook.name);//ajax
- console.log(newbook.book);//[ 'js', 'css', 'Node', 'React' ]
- console.log(otherbook.name);//xml
- console.log(otherbook.book);//[ 'js', 'css', 'Node', 'React' ]
- console.log(book.name);//javascript
- console.log(book.book);//[ 'js', 'css', 'Node', 'React' ]
六.寄生组合式继承
简介:寄生式继承和构造函数式继承结合使用。
- //寄生组合式继承
- function inheritObject(o) {
- //声明过渡函数对象
- function F() {}
- //过渡对象的原型继承父类
- F.prototype = o;
- return new F();
- }
- //寄生式继承 继承原型
- function inheritPrototype(subClass,superClass) {
- //复制一份父类的原型副本保存在变量中
- var p = inheritObject(superClass.prototype);
- //修正因为重写子类原型导致子类的constructor属性被修改
- p.constructor = subClass;
- //设置子类的原型
- subClass.prototype = p;
- }
- function SuperClass(name) {
- this.name = name;
- this.colors = ["red","blue","green"];
- }
- //定义父类原型方法
- SuperClass.prototype.getName = function() {
- console.log(this.name);
- }
- function SubClass(name,time) {
- SuperClass.call(this,name);
- this.time = time;
- }
- //寄生式继承父类原型
- inheritPrototype(SubClass,SuperClass);
- //子类新增原型方法
- SubClass.prototype.getTime = function() {
- console.log(this.time);
- }
- //测试
- var ins1 = new SubClass("js",2014);
- var ins2 = new SubClass("css",2015);
- ins1.colors.push("black");
- console.log(ins1.colors);
- console.log(ins2.colors);
- ins2.getName();
- ins2.getTime();
- from http://blog.csdn.net/qq_31280709/article/details/52137086
版本二、大牛的个人总结贴
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>继承</title>
</head>
<body>
<script>
/**
* [使用对象冒充实现继承(多继承)]
*/
function Parent(name) {
this.name = name
this.sayName = function() {
console.log(this.name)
}
}
var iParent = new Parent('james')
iParent.sayName()
function Child(name) {
this.parent = Parent
this.parent(name)
delete this.parent
this.saySome = function() {
console.log('my name: ' + this.name)
this.sayName()
}
}
var iChild = new Child('kobe')
iChild.saySome()
console.log(iChild.constructor)
/**
* Call/Apply方法改变函数上下文实现继承(该种方式不能继承原型链,若想继承原型链,则采用混合模式)
* 实现原理:改变函数内部的函数上下文this,使它指向传入函数的具体对象
*/
function Parent(firstname) {
this.fname = firstname;
this.age = 28;
this.sayAge = function() {
console.log(this.age);
}
}
function Child(firstname) {
Parent.call(this, firstname); // 将this传给父构造函数
//Parent.apply(this, [firstname]); // 与call作用相同
this.saySomeThing = function() {
console.log(this.fname);
this.sayAge();
}
this.getName = function() {
return firstname;
}
}
var myChild = new Child('Lee');
myChild.saySomeThing();
console.log(myChild.constructor)
myChild.constructor == Child; // true
/**
* 原型链实现继承
* 实现原理:使子类原型对象指向父类的实例以实现继承,即重写类的原型,弊端是不能直接实现多继承
*/
function Parent() {
this.sayAge = function() {
console.log(this.age);
}
}
function Child(firstname) {
this.fname = firstname;
this.age = 28;
this.saySomeThing = function() {
console.log(this.fname);
this.sayAge();
}
}
Child.prototype = new Parent();
var myChild = new Child('Lee');
myChild.saySomeThing(); // Lee 28
/**
* 混合模式
*/
function Parent() {
this.sayAge = function() {
console.log(this.age);
}
}
Parent.prototype.sayParent = function() {
alert('This is parent!');
}
function Child(firstname) {
Parent.call(this);
this.fname = firstname;
this.age = 28;
this.saySomeThing = function() {
console.log(this.fname);
this.sayAge();
}
}
Child.prototype = new Parent();
var myChild = new Child('Lee');
myChild.saySomeThing();
myChild.sayParent();
console.log(myChild.constructor)
function Parent(hello) {
this.hello = hello;
}
Parent.prototype.sayHello = function() {
alert(this.hello);
}
function Child(hello, world) {
Parent.call(this, hello); //将父类的属性继承过来
this.world = world; //新增一些属性
}
Child.prototype = new Parent(); //将父类的方法继承过来
Child.prototype.sayWorld = function() { //新增一些方法
alert(this.world);
}
var c = new Child("zhangsan", "lisi");
c.sayHello();
c.sayWorld();
/**
* es6继承
*/
class Animal {
//构造函数
constructor(props) {
this.name = props.name || '未知';
}
eat() {
alert(this.name + "在吃东西...");
}
}
//class继承
class Bird extends Animal {
//构造函数
constructor(props) {
//调用实现父类的构造函数
super(props);
this.type = props.type || "未知";
}
fly() {
alert(this.name + "在飞...");
}
}
var myBird = new Bird({
name: '鹦鹉'
})
myBird.eat()
myBird.fly()
</script>
</body>
</html>