欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Javascript OOP之面向对象

程序员文章站 2023-11-06 19:34:16
面向对象程序设计(object-oriented programming,oop)是一种程序设计范型,同时也是一种程序开发的方法。对象指的是类的实例。它将对象作为程序的基本...

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