一、面向对象的特性
- 封装:将属性和方法封装到一个类中,就是封装的过程。
- 继承:继承是面向对象中非常重要的,不仅仅可以减少重复代码的数量,也是多态前提(纯面向对象中)
- 多态:不同的对象在执行时表现出不同的形态。
二、原型链
var obj = {
name: 'xt'
}
obj.__proto__ = {}
obj.__proto__.__proto__ = {}
obj.__proto__.__proto__.__proto__ = {
age: 18
}
console.log(obj.age); // 18
上面的代码,在obj中是没有age属性的,但是当我们在其__proto__上添加了age后他是可以找到的,说明他的查找,是一层一层的在查找。如下图:
我们知道了其查找规则,但是他会不会一直找了,答案是不会,因为他是有顶层的,为什么我们能确定他是有顶层的呢?
var obj = {
name: 'xt'
}
console.log(obj.age); // undefined
因为当obj 中没age属性时,我们直接打印obj.age,他是能马上给我们返回undefined的,那他的顶层又是什么呢?
var obj = {
name: 'xt'
}
console.log(obj.__proto__); // [Object: null prototype] {}
[Object: null prototype] {} 这个就是我们的顶层,这个是在node环境下的打印,看着不太明显,我们可以在浏览器中执行(浏览器中他为了我们方便调试,是有帮我们特殊处理的,所以我们会更容易看懂)
看这里的__proto__是null,其实我们对象原型的顶层,就是null
我们刚看了对象的,那我们在来看看函数的
function foo(params) {
}
console.log(foo.prototype.__proto__.__proto__);// null
我们可以看到这里依旧是null,同时我们也应该知道函数的prototype属性值中,也是有__proto__的。当然,我们还可以知道,这里的foo其实是继承Object的,所以原型链最顶层的原型对象就是Object的原型对象
三、继承
继承的主要目的,就是为了减少重复代码。 当我们学完了,原型链其实我们就可以通过原型链来自己实现继承。
// 父类
function Person() {
this.name = 'xt'
}
Person.prototype.eating = function() {
console.log(this.name + 'eating');
}
// 子类
function Student() {
this.sno = 1
}
// 通过原型继承
Student.prototype = new Person()
Student.prototype.studying = function() {
console.log(this.name + 'studying');
}
var stu = new Student()
console.log(stu.name); // xt
通过这种方法实现继承的弊端
- 当我们打印stu的时候(node中),我们是看不到name这个属性的,因为name在person中。
- 如果是引用类型,可能会有传值引用问题。
- 不能给Person传递参数
如何解决
借用构造函数继承
function Person(name) {
this.name = 'xt'
}
...
Student.prototype.studying = function() {
Person.call(this, name)
console.log(this.name + 'studying');
}
...
虽然,我们前面的弊端解决了,但是,又有其他弊端,比如:
- Person至少要被调两次
- stu原型对象会多出一些属性,但是这些属性时没有存在的必要的
如何解决
寄生组合继承
// 父类
function Person(name) {
this.name = name
}
Person.prototype.eating = function() {
console.log(this.name + 'eating');
}
// 子类
function Student(name, sno) {
Person.call(this, name)
this.sno = sno
}
// 寄生式核心代码
function inheritPrototype(subType, superType) {
subType.prototype = Object.create(superType.prototype)
subType.prototype.constructor = subType
}
inheritPrototype(Student, Person)
Student.prototype.studying = function() {
console.log(this.name + 'studying');
}
var stu = new Student('xt', 1)
console.log(stu); // Student { name: 'xt', sno: 1 }