资讯专栏INFORMATION COLUMN

Javascript重温OOP之面向对象

AbnerMing / 983人阅读

摘要:类的继承建立继承关系修改的指向调用父类方法调用父类的构造器调用父类上的方法封装命名空间是没有命名空间的,因此可以用对象模拟。参考资料面向对象

面向对象程序设计(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
参考资料

JavaScript 面向对象

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90832.html

相关文章

  • Javascript重温OOPJS的解析与执行过程

    摘要:了解面向对象编程之前,首先要了解的执行顺序。的解析过程分为两个阶段预处理阶段与执行期。在执行阶段的执行上下文对象由赋值为指向对应函数 了解js面向对象编程之前,首先要了解js的执行顺序。js的解析过程分为两个阶段:预处理阶段与执行期。 预处理阶段 在预处理阶段,js会首先创建一个执行上下文对象(Execute Context,然后扫描声明式函数和用var定义的变量,将其加入执行上下文环...

    xumenger 评论0 收藏0
  • 重温基础】15.JS对象介绍

    摘要:构造函数通常首字母大写,用于区分普通函数。这种关系常被称为原型链,它解释了为何一个对象会拥有定义在其他对象中的属性和方法。中所有的对象,都有一个属性,指向实例对象的构造函数原型由于是个非标准属性,因此只有和两个浏览器支持,标准方法是。 从这篇文章开始,复习 MDN 中级教程 的内容了,在初级教程中,我和大家分享了一些比较简单基础的知识点,并放在我的 【Cute-JavaScript】系...

    booster 评论0 收藏0
  • Javascript重温OOP原型与原型链

    摘要:在构造函数中的中定义的属性和方法,会被创建的对象所继承下来。从上面的输出结果看出,指向了其构造函数的,而本身也是一个对象,其内部也有属性,其指向的是直到最后指向,这条原型链才结束。和都指向,说明原型链到终止。 prototype原型对象 每个函数都有一个默认的prototype属性,其实际上还是一个对象,如果被用在继承中,姑且叫做原型对象。 在构造函数中的prototype中定义的属性...

    lindroid 评论0 收藏0
  • Javascript重温OOP作用域与闭包

    摘要:的变量作用域是基于其特有的作用域链的。需要注意的是,用创建的函数,其作用域指向全局作用域。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。 作用域 定义 在编程语言中,作用域控制着变量与参数的可见性及生命周期,它能减少名称冲突,而且提供了自动内存管理 --javascript 语言精粹 我理解的是,一个变量、函数或者成员可以在代码中访问到的范围。 js的变量作...

    JessYanCoding 评论0 收藏0
  • Javascript重温OOP类与对象

    摘要:对构造函数使用运算符,就能生成实例,并且变量会绑定在实例对象上。这个对象的所有属性和方法,都会被构造函数的实例继承。 对象 对象的含义 所谓对象,就是一种无序的数据集合,由若干个键值对(key-value)构成。 对象的创建 使用new运算符创建Object var p = new Object(); p.name = Tony; 使用对象字面量的形式 //对象字面量形...

    RancherLabs 评论0 收藏0

发表评论

0条评论

AbnerMing

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<