关闭

js原型链,Javascript重温OOP之原型与原型链

时间: 2017-11-03阅读: 1327标签: js知识作者: Allin_Lin

面向对象程序设计(Object-oriented programming,OOP)是一种程序设计范型,同时也是一种程序开发的方法。对象指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。——维基百科

一般面向对象包含:继承,封装,多态,抽象

对象形式的继承

浅拷贝

var Person = {
    name: 'allin',
    age: 18,
    address: {
        home: 'home',
        office: 'office',
    }
    sclools: ['x','z'],
};

var programer = {
    language: 'js',
};

function extend(p, c){
    var c = c || {};
    for( var prop in p){
        c[prop] = p[prop];
    }
}
extend(Person, programer);
programer.name;  // allin
programer.address.home;  // home
programer.address.home = 'house';  //house
Person.address.home;  // house

从上面的结果看出,浅拷贝的缺陷在于修改了子对象中引用类型的值,会影响到父对象中的值,因为在浅拷贝中对引用类型的拷贝只是拷贝了地址,指向了内存中同一个副本。

深拷贝

function extendDeeply(p, c){
    var c = c || {};
    for (var prop in p){
        if(typeof p[prop] === "object"){
            c[prop] = (p[prop].constructor === Array)?[]:{};
            extendDeeply(p[prop], c[prop]);
        }else{
            c[prop] = p[prop];
        }
    }
}

利用递归进行深拷贝,这样子对象的修改就不会影响到父对象。

extendDeeply(Person, programer);
programer.address.home = 'allin';
Person.address.home; // home

利用call和apply继承

function Parent(){
    this.name = "abc";
    this.address = {home: "home"};
}
function Child(){
    Parent.call(this);
    this.language = "js"; 
}

ES5中的Object.create()

var p = { name : 'allin'};
var obj = Object.create(o);
obj.name; // allin

Object.create()作为new操作符的替代方案是ES5之后才出来的。我们也可以自己模拟该方法:

//模拟Object.create()方法
function myCreate(o){
    function F(){};
    F.prototype = o;
    o = new F();
    return o;
}
var p = { name : 'allin'};
var obj = myCreate(o);
obj.name; // allin

目前,各大浏览器的最新版本(包括IE9)都部署了这个方法。如果遇到老式浏览器,可以用下面的代码自行部署。

  if (!Object.create) {
    Object.create = function (o) {
       function F() {}
      F.prototype = o;
      return new F();
    };
  }

类的继承

Object.create()

function Person(name, age){}
Person.prototype.headCount = 1;
Person.prototype.eat = function(){
    console.log('eating...');
}
function Programmer(name, age, title){}

Programmer.prototype = Object.create(Person.prototype); //建立继承关系
Programmer.prototype.constructor = Programmer;  // 修改constructor的指向

调用父类方法

function Person(name, age){
    this.name = name;
    this.age = age;
}
Person.prototype.headCount = 1;
Person.prototype.eat = function(){
    console.log('eating...');
}

function Programmer(name, age, title){
    Person.apply(this, arguments); // 调用父类的构造器
}


Programmer.prototype = Object.create(Person.prototype);
Programmer.prototype.constructor = Programmer;

Programmer.prototype.language = "js";
Programmer.prototype.work = function(){
    console.log('i am working code in '+ this.language);
    Person.prototype.eat.apply(this, arguments); // 调用父类上的方法
}

封装

命名空间

js是没有命名空间的,因此可以用对象模拟。

var app = {};  // 命名空间app
//模块1
app.module1 = {
    name: 'allin',
    f: function(){
        console.log('hi robot');
    }
};
app.module1.name; // "allin"
app.module1.f();  // hi robot

静态成员

function Person(name){
    var age = 100;
    this.name = name;
}
//静态成员
Person.walk = function(){
    console.log('static');
};
Person.walk();  // static

私有与公有

function Person(id){
    // 私有属性与方法
    var name = 'allin';
    var work = function(){
        console.log(this.id);
    };
    //公有属性与方法
    this.id = id;
    this.say = function(){
        console.log('say hello');
        work.call(this);
    };
};
var p1 = new Person(123);
p1.name; // undefined
p1.id;  // 123
p1.say();  // say hello 123

模块化

var moduleA;
moduleA = function() {
    var prop = 1;

    function func() {}

    return {
        func: func,
        prop: prop
    };
}(); // 立即执行匿名函数

prop,func 不会被泄露到全局作用域。或者另一种写法,使用 new

moduleA = new function() {
    var prop = 1;

    function func() {}

    this.func = func;
    this.prop = prop;
}

多态

模拟方法重载

arguments属性可以取得函数调用的实参个数,可以利用这一点模拟方法的重载。

function demo(a, b ){
    console.log(demo.length); // 得到形参个数
    console.log(arguments.length); //得到实参个数
    console.log(arguments[0]);  // 第一个实参 4
    console.log(arguments[1]);  // 第二个实参 5
}

demo(4, 5, 6);
//实现可变长度实参的相加
function add(){
    var total = 0;
    for( var i = arguments.length - 1; i >= 0; i--){
        total += arguments[i];
    }
    return total;
}

console.log(add(1));  // 1
console.log(add(1, 2, 3));  // 7


// 参数不同的情况
function fontSize(){
    var ele = document.getElementById('js');
    if(arguments.length == 0){
        return ele.style.fontSize;
    }else{
        ele.style.fontSize = arguments[0];
    }
}
fontSize(18);
console.log(fontSize());

// 类型不同的情况
function setting(){
    var ele = document.getElementById('js');
    if(typeof arguments[0] === "object"){
        for(var p in arguments[0]){
            ele.style[p] = arguments[0][p];
        }
    }else{
        ele.style.fontSize = arguments[0];
        ele.style.backgroundColor = arguments[1];
    }
}
setting(18, 'red');
setting({fontSize:20, backgroundColor: 'green'});

方法重写

function F(){}
var f = new F();
F.prototype.run = function(){
    console.log('F');
}
f.run(); // F

f.run = function(){
    console.log('fff');
}
f.run();  // fff

抽象类

在构造器throw new Error('');中抛异常。这样防止这个类被直接调用。

function DetectorBase() {
    throw new Error('Abstract class can not be invoked directly!');
}

DetectorBase.prototype.detect = function() {
    console.log('Detection starting...');
};
DetectorBase.prototype.stop = function() {
    console.log('Detection stopped.');
};
DetectorBase.prototype.init = function() {
    throw new Error('Error');
};

// var d = new DetectorBase();// Uncaught Error: Abstract class can not be invoked directly!

function LinkDetector() {}
LinkDetector.prototype = Object.create(DetectorBase.prototype);
LinkDetector.prototype.constructor = LinkDetector;

var l = new LinkDetector();
console.log(l); //LinkDetector {}__proto__: LinkDetector
l.detect(); //Detection starting...
l.init(); //Uncaught Error: Error

转载地址:http://www.jianshu.com/p/ac3e6c88987a

站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: http://www.fly63.com/article/detial/49

一眼看穿JS变量,作用域和内存问题

这篇文章将梳理下环境,作用域链,变量对象和活动对象,以及内存管理问题。基本类型和引用类型的值,我们都知道JS中的数据类型有两大类,基本数据类型和引用数据类型,下面从三个方面来解剖他们

Js中的命名空间(namespace)

全局变量应该由有系统范围相关性的对象们保留,并且它们的命名应该避免含糊并尽量减少命名冲突的风险。在实践中,这意味着你应该避免创建全局对象,除非它们是绝对必须的。 所以你对此是怎么做的?传统方法告诉我们,最好的消除全局策略是创建少数作为潜在模块和子系统的实际命名空间的全局对象。

javascript中的依赖注入

使用没有依赖的模块,显然这是很难实现的。即使你创建了很好的像黑盒一样的组件,但总有个将所有部分合并起来的地方。这就是依赖注入起作用的地方,当前来看,高效管理依赖的能力是迫切需要的,本文总结了原作者对这个问题的看法。

(...)这三个点在JavaScript中意味着什么?

解释JavaScript中三个点的作用:数组/对象扩展运算符、rest运算符(使用函数的参数时,无论是完全替换参数还是与函数的参数一起替换参数,这三个点也称为rest运算符)

为什么JavaScript开发如此疯狂

Web开发中其他一切对你而言都是小菜一碟,但当你深入探究JavaScript的时候,不免有一种“众人皆醒你独醉”的悲壮——好像其他人都知道你不知道的一些大的基础的知识内容,并且这些内容可以帮助你理解所有知识

44道JS难题

国外某网站给出了44道JS难题,这些题涉及面非常广,涵盖JS原型、函数细节、强制转换、闭包等知识,而且都是非常细节的东西,透过这些小细节可以折射出很多高级的JS知识点。

js dom是什么?_JS中的DOM知识概览

JS中的DOM知识概览:文档对象模型,是针对 HTML 和 XML 文档的一个 API (应用程序编程接口), 描绘了一个层次化的节点树。 web 浏览器浏览一个页面的时候,DOM 就在幕后把你编辑的网页文档转换成一个文档对象。

js不同数据类型下的toString()与toLocaleString()的输出差异

toString()与toLocaleString()方法主要针对对象Object转换为字符串,如果是基本类型调用它们的时候,先会把基本类型实例化为对应的对象类型,然后在转换为字符串。这篇文章主要讲解不同数据类型下的toString()与toLocaleString()的输出差异。

JavaScript 优雅的实现方式包含你可能不知道的知识点【转】

Js优雅的实现方式包含你可能不知道的知识点:简短优雅地实现 sleep 函数,js获取时间戳,js数组去重,js数字格式化,js交换两个整数,将 argruments 对象(类数组)转换成数组,数字取整等

js记录用户行为浏览记录和停留时间

原来是想使用 cookie 来记录,但是考虑到 cookie 所能记录的数据最大为 4k ,可能不够用。于是使用了 HTML5 的 localStorage (最大数据 5M )来存储( IE8 以上浏览器支持)。这里使用到了 jquery.cookie 的插件,所以页面要引入 jquery 和 jquery.cookie 。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!