面向对象与原型链
类的声明和实例化
//、类的声明
var Animal = function () {
this.name = 'Animal';
};
//es6中class的声明
class Animal2 {
constructor () {
this.name = 'Animal2';
}
}
//实例化
console.log(new Animal(), new Animal2());
继承
代码的抽象和代码的复用
原型链继承
<script>
function Parent(){
this.name = 'mike';
}
function Child(){
this.age = 12;
}
Child.prototype = new Parent();//Child继承Parent,通过原型,形成链条
var test = new Child();
</script>
问题1:一是字面量重写原型会中断关系,使用引用类型的原型,
问题2:子类型还无法给父类型传递参数
构造函数继承
function Box(age){
this.name=['Lee','Jack','Hello']
this.age=age;
}
function Desk(age){
Box.call(this,age); //对象冒充,给超类型传参
}
var desk = new Desk(200);
组合继承
原型链+借用构造函数的模式
<script>
function Parent(age){
this.name = ['mike','jack','smith'];
this.age = age;
}
Parent.prototype.run = function () {
return this.name + ' are both' + this.age;
};
function Child(age){
Parent.call(this,age);//对象冒充,给超类型传参
}
Child.prototype = new Parent();//原型链继承
var test = new Child(21);//写new Parent(21)也行
</script>
组合继承改进
function Parent5 () {
this.name = 'parent5';
this.play = [1, 2, 3];
}
function Child5 () {
Parent5.call(this);
this.type = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype);
Child5.prototype.constructor = Child5;
Object.create()实现
function object(o) {
function F() {};
F.prototype = o;
return new F();
}
Son.prototype = object(Parent.prototype);
ES6
class Man extends Person{
constructor(name){
super(name)//3
this.sex="male"
}
}
多态
就是子类重写父类的属性或者方法
Parent.prototype.play = function() {
console.log(this.name);
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
// 多态
Child.prototype.play = function() {
Parent.prototype.play.call(this);
console.log(this.age);
}
super关键字
使用
第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数
class A {}
class B extends A {
constructor() {
super();
}
}
super()在这里相当于A.prototype.constructor.call(this) 就是 Parent.call(this, name);
第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
super.p()就相当于A.prototype.p()
创建对象的几种方式
对象的定义:“无序属性的集合,其属性可以包括基础值、对象或者函数”,既可以是普通对象(Object),也可以是函数对象(Function)。凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。Function Object 也都是通过 New Function()创建的
// 第一种方式:字面量
var o1 = {name: 'o1};
var o11 = new Object({name: 'o11});
// 第二种方式:构造函数
var M = function(){this.name='o2'};
var o2 = new M();
// 第三种方式:Object.create
var P = {name: 'o3'};
var o3 = Object.create(P);
原型、构造函数、实例、原型链
构造函数:
构造函数可用来创建特定类型的对象。
原型:
我们创建的每个函数都有一个prototype(原型)属性。那个属性是一个指针,指向一个对象(原型对象),原型对象的用途是让所有实例共享属性和方法。普通对象没有 prototype,但有 proto 属性。
原型对象
prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。
- 每当我们创建一个新函数。就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。
- 在默认的详情下。所有原型对象都会自动获得一个constructor(构造函数)属性。
- 那个属性包含一个指向prototype属性所在函数的指针。通过那个构造函数为原型对象添加其他属性和方法。
什么是原型链
如果想要找到一个对象的属性,首先会先在自身查找,如果没有,就会通过__proto__属性一层层的向上查找,
直到原型链的顶端 Object.prototype(proto: null)
这种通过某种纽带(proto)将对象之间形成一种继承关系 这种关系呈现出一种链条的形状 将这种链条称之为原型链
构造函数、原型和实例的关系
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针。而实例都包含一个指向原型对象的内部指针。让原型对象等于另一个类型的实例,原型对象将包含一个指向另一个原型的指针,对应地,另一个原型中包含着一个指向另一个构造函数的指针。
instanceof的原理
原型和实例之间的关系就需要用到 instanceof 操作符
function instanceOf( L, R ) { //L 表示左表达式,R 表示右表达式
var P = R.prototype; // 取 R 的显示原型
L = L.__proto__; // 取 L 的隐式原型
while ( true ) {
if ( L === null ) return false;
if ( P === L ) return true;
L = L.__proto__;
}
}
instanceof 会一直在 obj 的原型链上查找,直到找到右边那个构造函数的 prototype 属性,或者为 null 的时候才停止。
obj.__proto__.__proto__ ... = Obj.prototype
obj 会一直沿着隐式原型链 __proto__ 向上查找直到
obj.__proto__.__proto__ ...... === Obj.prototype 为止,
如果找到则返回 true,也就是 obj 为 Obj 的一个实例。否则返回 false,obj 不是 Obj 的实例。
new运算符
伪代码模拟:
new Animal("cat") = {
var obj = {};//创建一个空对象obj;
obj.__proto__ = Animal.prototype;
//把obj的__proto__ 指向Animal的原型对象prototype,此时便建立了obj对象的原型链
//:obj->Animal.prototype->Object.prototype->null
var result = Animal.call(obj,"cat");
//在obj对象的执行环境调用Animal函数并传递参数“cat” 改变this的指向
return typeof result === 'obj'? result : obj;
//考察第3步返回的返回值,如果无返回值或者返回一个非对象值,
//则将obj返回作为新对象;否则会将返回值作为新对象返回。
}
- 创建一个全新的对象。
- 这个对象会被执行[[Prototype]]连接。
- 这个新对象会绑定到函数调用的this。
- 执行这个函数里的代码。
- 如果函数没有返回其他对象,则自动返回这个新对象。
this指向问题
- 默认绑定,this绑定到全局对象
- 隐式绑定,这个函数调用时是用一个对象
- 显式绑定,apply和call函数改变this指向
- new绑定,执行new操作的时候对this的绑定
- 箭头函数的this,外层的作用域来决定this
绑定规则的优先级
- 如果函数在new中调用,绑定到新建的对象。
- 函数通过call或apply或者硬绑定调用,this绑定到指定的对象上。
- 函数在某个上下文对象中调用,绑定到这个上下文对象上。
- 采用默认绑定规则。
浅拷贝和深拷贝
浅复制--->就是将一个对象的内存地址的“”编号“”复制给另一个对象。深复制--->实现原理,先新建一个空对象,内存中新开辟一块地址,把被复制对象的所有可枚举的(注意可枚举的对象)属性方法一一复制过来,注意要用递归来复制子对象里面的所有属性和方法,直到子子.....属性为基本数据类型。总结,深复制理解两点,1,新开辟内存地址,2,递归来刨根复制。
浅拷贝代码实现:
function shallowClone(copyObj) {
var obj = {};
for ( var i in copyObj) {
obj[i] = copyObj[i];
}
return obj;
}
Object.assign()
if (typeof Object.assign != 'function') {
// Must be writable: true, enumerable: false, configurable: true
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}
深拷贝代码实现:
JSON.parse(JSON.stringify(source));
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if(! p.hasOwnProperty(i)){
continue;
}
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}