Js 面向对象之封装,继承

封装 ,继承 
 
封装 ?
面向对象有三大特性,封装、继承和多态。对于ES5来讲,没有class(类)的概念,而且因为JS的函数级做用域(函数内部的变量在函数外访问不到),因此咱们就能够模拟 class (类)的概念,在ES5中,类其实就是保存了一个函数的变量,这个函数有本身的属性和方法;将属性和方法组成一个类的过程就是封装。
那么,若是咱们要把"属性"(property)和"方法"(method),封装成一个对象,甚至要从原型对象生成一个实例对象,咱们应该怎么作呢?
 
原始模式
function Cat (name, color) {
  return {
    name: name,
    color: color
  }
}
var cat1 = Cat("大毛", "黄色");//{name: "大毛", color: "黄色"}
var cat2 = Cat("二毛", "黑色");//{name: "二毛", color: "黑色"}

这种模式并不能看出来 cat1 和 cat2 是同一个原型对象的实例css

构造函数模式html

为了解决从原型对象生成实例的问题,Js提供了一个构造函数(Constructor)模式。
所谓"构造函数",其实就是一个普通函数,可是内部使用了this变量,对构造函数使用new运算符,就能生成实例,而且this变量会绑定在实例对象上
 function Cat (name, color) {
    this.name = name;
    this.color = color;
    this.age = "5";
 }
 var cat1 = new Cat("大毛", "黄色");
 var cat2 = new Cat("二毛", "黑色");
  cat1.constructor == Cat;//true
  cat2.constructor == Cat; //true
  cat1.constructor == cat2.constructor//true
JS 还提供了 instanceof 运算符,用来校验原型对象与实例对象的关系 
cat1 instanceof Cat ;// true
表面上好像没什么问题,可是实际上这样作,有一个小小的不优雅,那就是对于每个实例对象,age属性都会生成一遍,每生成一个实例,都必须为重复的内容,多占用一些内存,这样既不环保,也缺少效率。那么有办法解决吗 ? 答案固然是有的:
 
Prototype模式
Javascript规定,每个构造函数都有一个prototype属性,指向另外一个对象。这个对象的全部属性和方法,都会被构造函数的实例继承。这意味着,咱们能够把那些不变的属性和方法,直接定义在prototype对象上;
 function Cat (name, color) {
    this.name = name;
    this.color = color;
 }
  Cat.prototype.age = "10";
 var cat1 = new Cat("大毛", "黄色");
 var cat2 = new Cat("二毛", "黑色");
  cat1.age; // 10;
  cat2.age; // 10;
这时全部实例的age属性,其实都是同一个内存地址,指向prototype对象,所以就提升了运行效率;
 
继承 ?
 
Prototype 原型继承
// 建立父构造函数
function SuperClass(name){
  this.name = name;
  this.showName = function(){
    alert(this.name);
  }
}
// 设置父构造器的原型对象
SuperClass.prototype.Age = '123';
// 建立子构造函数
function SubClass(){}
// 设置子构造函数的原型对象实现继承
SubClass.prototype = SuperClass.prototype;
//生成实例
var child = new SubClass()

child.name // undefined
child.Age // 123
 
上述的Prototype模式已经实现了继承,但仅仅继承了父构造函数的prototype 原型上的成员,并不能继承父构造函数的其它成员,那么还有别的方法来实现继承嘛 ? 答案固然是有的;
 
原型链继承
 
//即 子构造函数.prototype = new 父构造函数()
// 建立父构造函数
function SuperClass(){
    this.name = 'HiSen';
    this.age = 25;
    this.showName = function(){
        console.log(this.name);
    }
}
// 设置父构造函数的原型
SuperClass.prototype.friends = ['js', 'css'];
SuperClass.prototype.showAge = function(){
    console.log(this.age);
}
// 建立子构造函数
function SubClass(){}
// 实现继承
SubClass.prototype = new SuperClass();
// 修改子构造函数的原型的构造器属性,由于此时继承了父构造函数指向 //SuperClass; 因此要修改一下。
SubClass.prototype.constructor = SubClass;

//生成实例
var child = new SubClass();
console.log(child.name);
// HiSen console.log(child.age);// 25 child.showName();// HiSen child.showAge();// 25 console.log(child.friends); // ['js','css'] // 当咱们改变friends的时候, 父构造函数的原型对象的也会变化 child.friends.push('html'); console.log(child.friends);// ["js", "css", "html"] var father = new SuperClass(); console.log(father.friends);// ["js", "css", "html"]

此时再看:发现子构造函数 不只继承了父构造函数原型 prototype 上的成员,也继承了其它成员。但是修改子构造函数的属性时,咱们发现父构造函数的原型对象也对应修改,那有没有办法屏蔽这一种状况呢 ? 接着往下看:数组

 

拷贝实现继承函数

说到拷贝,可能会分深拷贝和浅拷贝,其实:this

浅拷贝是对象的属性的引用,而不是对象自己; (浅拷贝只拷贝一层,若是存在多层仍是会影响原对象)spa

深拷贝是建立一个新的内存地址保存值 ; (与原对象互不影响)prototype

下边我列举两个拷贝的方法来实践一下:code

浅拷贝htm

例举一个简单的浅拷贝: 对象形式对象

function clone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
            target[i] = source[i];
        }
    }
    return target;
}

深拷贝

对象形式的深拷贝

function clone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
            if (typeof source[i] === 'object') {
                target[i] = clone(source[i]); // 注意这里
            } else {
                target[i] = source[i];
            }
        }
    }

    return target;
}

 数组形式的深拷贝

function clone(source) {
      var out = [],i = 0,len = source.length;
      for (; i < len; i++) {
      if (source[i] instanceof Array){
        out[i] = clone(arr[i]);
      }
        else out[i] = source[i];
      }
      return out;
}

 

固然出了以上这些实现继承的方法之外还有更多的方式一样能够实现继承,例如:

Object.create();继承

ECMAScript 5 中引入了一个新方法: Object.create()。能够调用这个方法来建立一个新对象。新对象的原型就是调用 create 方法时传入的第一个参数:
 
Object.create()方法接受两个参数:Object.create(obj,propertiesObject) ;
obj:一个对象,应该是新建立的对象的原型。
propertiesObject:可选。该参数对象是一组属性与值,该对象的属性名称将是新建立的对象的属性名称,值是属性描述符(这些属性描述符的结构与 Object.defineProperties()的第二个参数同样)。注意:该参数对象不能是  undefined,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的。
 
var obj = {
    "a":'123', fun :function () { alert(1) } } 
var jc = Object.create(obj); jc.a; //123 jc.fun();//1

咱们能够看到,jc 继承了 obj 的属性;同时也继承了 obj 对象的方法;

ES6钟提供了一个方法 Object.assign();

Object.assign是ES6的新函数。Object.assign() 方法能够把任意多个的源对象自身的可枚举属性拷贝给目标对象,而后返回目标对象。

Object.assign(target, ...sources)

target:目标对象。
sources:任意多个源对象。

var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);

initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"

 

最后再说一种最简单的方式,转成字符串 - 再转回来;

var obj1 = { o: { a: 1 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
 
ECMAScript6 引入了一套新的关键字用来实现 class。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不一样的。JavaScript 仍然基于原型。这些新的关键字包括 class, constructor,static,extends 和 super。敬请期待...
相关文章
相关标签/搜索