JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】


Posted in Javascript onMay 12, 2020

本文实例讲述了JavaScript 面向对象程序设计。分享给大家供大家参考,具体如下:

类的创建于实例对象

工厂模型创建对象

function CreatePerson ( name,sex,age ) {
 
 var obj = new Object();
 
 obj.name = name;
 obj.sex = sex;
 obj.age = age;
 
 obj.sayName = function () {
  
  console.log( this.name );
  
 }
 
 return obj;
 
}

var p1 = CreatePerson('zf','女',22);

p1.sayName(); //zf

console.log( p1.name ); //zf

构造函数式

//函数的第一个字母大写(类的模板)
function Person ( name,age,sex ) {
 
 this.name = name;
 this.age = age;
 this.sex =sex;
 
 this.sayName = function () {
  
  alert(this.name);
  
 }
 
}

//构造一个对象, 使用new关键字, 传递参数, 执行模板代码, 返回对象。

var p1 = new Person('zf',20,'女'); //类的概念:根据模板创建出不同的实例对象
 
console.log( p1.name );

p1.sayName();

创建类的实例:

  • 当作构造函数去使用

    var p1 = new Person('a1',20);

  • 作为普通函数去调用

    Person('a2',20); //在全局环境中定义属性并赋值, 直接定义在window上。

  • 在另个一对象的作用域中调用

    var o = new Object();
    Person.call(o,'a3',23);

Object每个实例都会具有的属性和方法:

Constructor: 保存着用于创建当前对象的函数。(构造函数)
hasOwnProperty(propertyName):用于检测给定的属性在当前对象实例中(而不是原型中)是否存在。
isPrototypeOf(Object): 用于检查传入的对象是否是另外一个对象的原型。
propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用for-in语句来枚举。
toLocaleString():返回对象的字符串表示。该字符串与执行环境的地区对应.
toString():返回对象的字符串表示。
valueOf():返回对象的字符串、数值或布尔表示。

判断一个对象是不是另一个对象的实例,通常使用的是 instanceof. 比较少使用constructor。

原型

创建每一个函数的时候,都有一个prototype属性. 这个是属性,是一个指针。而这个对象总是指向一个对象。
这个对象 的用途就是将特定的属性和方法包含在内,是一个实例对象, 起到了一个所有实例所共享的作用。
屏蔽了,构造函数的缺点,new 一个对象,就把构造函数内的方法实例化一次。

function Person () {
    
}

var obj = Person.prototype;

console.log( obj ); //Person.prototype 就是一个对象
//Person.prototype 内部存在指针,指针指向一个对象。 这个对象称之为:原型对象。原型对象,被所有的实例对象所共享。

console.log( obj.constructor ); //function Person(){} //obj这个对象的构造器就是 Person

原型图例:

JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】

console.log(Person.prototype) 的结果:

JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】

常用方法

Object.getPrototypeOf()

根据实例对象获得原型对象

每次代码读取一个对象的属性的时候:首先会进行一次搜索,搜索实例对象里,看看是否存在,如果没有,再去实例所对的原型中寻找属性.如果有则返回,如果两次都没有则返回undefined

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型对象方法' );
 
}

var p1 = new Person();

console.log( p1.name ); //z1

console.log( Object.getPrototypeOf(p1) ); 
console.log( Object.getPrototypeOf(p1) == Person.prototype ); //true

hasOwnProperty()

判断是否是 实例对象自己的属性

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型对象方法' );
 
}


// 判断一个对象属性 是属于 原型属性 还是属性 实例属性

var p3 = new Person(); 
console.log( p3.name ); //zf 是原型上的

//hasOwnProperty() 是否是 实例对象自己的属性 
console.log( p3.hasOwnProperty('name') ); //false

in 操作符

无论是 原型的属性, 还是实例对象的属性, 都区分不开。 如果存在,返回true

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型对象方法' );
 
}


//判断属性是否存在 实例对象 和 原型对象中. 

var p1 = new Person();
console.log('name' in p1); //true //表示,name的属性到底在不在p1的属性中 true
var p2 = new Person();

p1.name = 'zzz';

console.log('name' in p1); //true

判断一个属性是否在原型中

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型对象方法' );
 
}


//判断属性是否存在 实例对象 和 原型对象中. 

var p1 = new Person();
p1.name = '123';

//在原型对象中,是否存在这个值
//@obj 当前对象
//@判断的属性
function hasPrototypeProtoperty ( obj,attrName ) {
 
 return !obj.hasOwnProperty(attrName) && (attrName in obj);
 
}

console.log( hasPrototypeProtoperty(p1,'name') ); //false

Object.keys()

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型对象方法' );
 
}

//ECMA5新特性 Object.keys(); 
//拿到当前对象中的所有keys, 返回一个数组

var p1 = new Person();
p1.name = 'zz';
p1.age = 20;

var attr = Object.keys(p1);
console.log( attr ); //["name", "age"]

var attr2 = Object.keys(p1.__proto__); 

console.log( attr2 ); //["name", "age", "sayName"]

var attr3 = Object.keys(Person.prototype); 

console.log( attr3 ); //["name", "age", "sayName"]

Object.getOwnPropertyNames()

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型对象方法' );
 
}

var p1 = new Person();
p1.name = 'zz';
p1.age = 20;

//ECMA5 
//constructor属性,是无法被枚举的. 正常的for-in循环是无法枚举. [eable = false];

//Object.getOwnPropertyNames(); //枚举对象所有的属性:不管该内部属性能够被枚举.

var attr4 = Object.getOwnPropertyNames(Person.prototype); //["constructor", "name", "age", "sayName"]

console.log( attr3 );

isPrototypeOf()

判断原型的方法

原型对象.isPrototypeOf(new instance);

实现each方法

原型的另外一个作用就是扩展对象中的属性和方法

//遍历多维数组
var arr = [1,2,4,5,[455,[456,[345345]]]];

Array.prototype.each = function ( cb ) {

 try {

  //计数器
  this.i || (this.i = 0);

  //核心代码
  if ( this.length > 0 && cb.constructor === Function ) {

   while ( this.i < this.length ) { //计数器 大于 数组长度跳出

    //获得每一项值 
    var e = this[this.i];

    //判断是否是 数组
    if ( e && e.constructor === Array ) {

     //递归
     e.each(cb);

    } else {

     cb.call(null,e);

    }

    this.i++; 

   }

   //使用完之后,释放变量
   this.i = null;

  }

 } catch (e) {
  //do someting
  
 }

 return this;

};

arr.each(function( val ){

 console.log(val);

});

简单原型

直接通过对象字面量来重写整个原型对象(这种方法会改变原型对象的构造器[改变为Object])

//简单原型
   
function Person () {
 
}

Person.prototype = {
 
 constructor: Person, //原型的构造器改变
 
 name: 'zz',
 age: 20,
 
 say: function () {
  console.log( this.age );
 }
 
}

var p1 = new Person();

console.log( p1.name );
p1.say();

存在的问题,constructor属性是无法被枚举的。加在原型对象上,可以被枚举,被枚举。不符合要求。

ECMA5中的Object.defineProperty()方法可以为原型对象重新加入构造器。constructor问题可以被避免。

//3个参数, 参数1:重新设置构造的对象 (给什么对象设置)  参数2:设置什么属性  参数3:options配置项 (要怎么去设置)
Object.defineProperty(Person.prototype,'constructor',{
 enumerable: false, //是否是 能够 被枚举
 value: Person //值 构造器的 引用
});

原型的动态特性

注意原型和创建实例的前后顺序

function Person () {
      
}

var p1 = new Person(); // {}
 
Person.prototype = {
 constructor: Person,
 name: 'zf',
 age: 20,
 say: function () {
  console.log('原型');
 }
}

//先把原型对象写好,然后再实例化。

//p1.say(); //error 因为  原型对象里面没有任何属性和方法

var p2 = new Person();

p2.say();

//注意 简单原型使用的顺序(实例对象必须在原型对象之后创建)

原型对象的常用开发模式

组合构造函数式和原型模式

function Person( name,age,firends ) {
 
 this.name = name;
 this.age = age;
 this.firends = firends;
 
}

Person.prototype = {
 constructor: Person,
 sayName: function () {
  console.log( this.name );
 }
}

var p1 = new Person('zz',20,['zf']);
var p2 = new Person('zx',22,['z1']);

console.log( p1.firends ); //['zf']    
console.log( p2.firends ); //['z1']

动态原型模式

就是把信息都封装到函数中,这样体现了封装的概念。

//动态原型模式:(让你的代码 都封装到一起)
function Person( name,age,firends ) {
 
 this.name = name;
 this.age = age;
 this.firends = firends;
 
 //动态原型方法
 if ( typeof this.sayName !== 'function' ) {
  
  Person.prototype.sayName = function () {
   console.log(this.name);
  }
  
 }
 
}

稳妥构造函数式

稳妥模式就是没有公共属性,而且其他方法也不引用this对象,稳妥模式最适合在安全的环境中使用。如果程序对于安全性要求很高,那么非常适合这种模式。
也不能使用new关键字。

//稳妥构造函数式 durable object (稳妥对象)
//1,没有公共的属性
//2,不能使用this对象

function Person ( name,age ) {
 
 //创建一个要返回的对象。 利用工厂模式思维。
 var obj = new Object();
 
 //可以定义一下是有的变量和函数 private
 
 var name = name || 'zf';
 
//      var sex = '女';
//      var sayName = function () {
//      }

  
 //添加一个对外的方法
 obj.sayName = function () {
  console.log(name);
 }
 
 return obj;
 
}

var p1 = Person('xixi',20);

p1.sayName();

深入原型继承的概念

如果让原型对象等于另一个类型的实例,结果会怎么样呢?显然此时的原型对象将包含一个指向另一个原型的指针,相应的另一个原型中也包含着一个指向另一个构造函数的指针。

原型链: 利用原型让一个引用类型继承另外一个引用类型的属性和方法。

构造函数 原型对象 实例对象

  • 构造函数.prototype = 原型对象

  • 原型对象.constructor = 构造函数

  • 实例对象.__proto__ = 原型对象

  • 原型对象.isPrototypeOf(实例对象)

  • 构造函数 实例对象 (类和实例)

isPrototypeOf(); //判断是否 一个对象的 原型

JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】

//父类的构造函数 Sup
function Sup ( name ) {
 
 this.name = name;
 
}

//父类的原型对象
Sup.prototype = {
 constructor: Sup,
 sayName: function () {
  console.log(this.name);
 }
}

//子类的构造函数 Sub
function Sub ( age ) {
 
 this.age = age;
 
}

//如果子类的原型对象 等于 父类的 实例

//1, 显然此时的原型对象将包含一个指向另一个原型的指针
//2, 相应的另一个原型中也包含着一个指向另一个构造函数的指针。

//   实例对象.__proto__ = 原型对象
//    Sup的实例对象 和 Sup的原型对象 有一个关系

Sub.prototype = new Sup('zf');

//   console.log( Sub.prototype.constructor ); //function Sup () {}
//   
//   console.log( Sub.prototype.__proto__ ); //Sup 的 原型对象


var sub1 = new Sub(20);

console.log( sub1.name ); //zf
sub1.sayName(); //zf

原型链继承映射图

JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】


JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】

继承的三种方式

原型继承

//原型继承的特点:
//即继承了父类的模板,又继承了父类的原型对象。 (全方位的继承)
//父类
function Person ( name,age ) {
 
 this.name = name;
 this.age = age;
 
}

Person.prototype.id = 10;

//子类
function Boy ( sex ) {
 
 this.sex = sex;  
 
}

//原型继承
Boy.prototype = new Person('zz');

var b = new Boy();

console.log( b.name ); //zz
console.log( b.id ); //10

类继承

类继承 (只继承模板) 不继承原型对象 (借用构造函数的方式继承)

//父类
function Person ( name,age ) {
 
 this.name = name;
 this.age = age;
 
}

Person.prototype.id = 10;

//子类
function Boy ( name,age,sex ) {
 
 //类继承
 Person.call(this,name,age);
 
 this.sex = sex;  
 
}

var b = new Boy('zf',20,'女');

console.log( b.name ); //zf
console.log( b.age ); //20
console.log( b.sex ); //女
console.log( b.id ); //父类的原型对象并没有继承过来.

混合继承

原型继承+类继承

//父类  (关联父类和子类的关系)
function Person ( name,age ) {
 
 this.name = name;
 this.age = age;
 
}

Person.prototype.id = 10;
Person.prototype.sayName = function () {
 console.log( this.name );
}

//子类
function Boy ( name,age,sex ) {
 
 //1 类继承
 Person.call(this,name,age); //继承父类的模板
 
 this.sex = sex;  
 
}    

//2 原型继承
//父类的实例 和 父类的 原型对象的关系.
Boy.prototype = new Person(); //继承父类的原型对象

var b = new Boy('z1',20,'女');

console.log( b.name );//z1
console.log( b.sex ); //女
console.log( b.id ); //10

b.sayName(); //z1

ExtJs底层继承方式

模拟ExtJs底层继承一部分代码

//ExtJs 继承
//2件事: 继承了1次父类的模板,继承了一次父类的原型对象


function Person ( name,age ) {
 
 this.name = name;
 this.age = age;
 
}

Person.prototype = {
 
 constructor: Person,
 
 sayHello: function () {

  console.log('hello world!');

 }
 
}

function Boy ( name,age,sex ) {
 
 //call 绑定父类的模板函数 实现 借用构造函数继承 只复制了父类的模板
 
//    Person.call(this,name,age);

  Boy.superClass.constructor.call(this,name,age);
 
 this.sex = sex;  
 
}

//原型继承的方式: 即继承了父类的模板,又继承了父类的原型对象。
//   Boy.prototype = new Person();
//只继承 父类的原型对象
 extend(Boy,Person); // 目的 只继承 父类的原型对象 , 需要那两个类产生关联关系.

//给子类加了一个原型对象的方法。
Boy.prototype.sayHello = function () {
 
 console.log('hi,js');
 
}

var b = new Boy('zf',20,'男');

console.log( b.name );
console.log( b.sex );
b.sayHello(); 

Boy.superClass.sayHello.call(b);

//extend方法

//sub子类, sup 父类
function extend ( sub,sup ) {
 
 //目的, 实现只继承 父类的原型对象。 从原型对象入手
 
 //1,创建一个空函数, 目的:空函数进行中转
 var F = new Function(); // 用一个空函数进行中转。
 
//    把父类的模板屏蔽掉, 父类的原型取到。
 
 F.prototype = sup.prototype; //2实现空函数的原型对象 和 超类的原型对象转换
 
 sub.prototype = new F(); //3原型继承
 
 //做善后处理。 还原构造器 ,
 sub.prototype.constructor = sub; //4 ,还原子类的构造器
 
//    保存一下父类的原型对象 // 因为 ①方便解耦, 减低耦合性  ② 可以方便获得父类的原型对象
 sub.superClass = sup.prototype; //5 ,保存父类的原型对象。 //自定义一个子类的静态属性 , 接受父类的原型对象。
 
 //判断父类的原型对象的构造器, (防止简单原型中给更改为 Object)
 if ( sup.prototype.constructor == Object.prototype.constructor ) {
  
  sup.prototype.constructor = sup; //还原父类原型对象的构造器
  
 }
 
}

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码运行效果。

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
个人总结的一些关于String、Function、Array的属性和用法
Jan 10 Javascript
Javascript 函数parseInt()转换时出现bug问题
May 20 Javascript
解决jquery版本冲突的有效方法
Sep 02 Javascript
jquery获取radio值实例
Oct 16 Javascript
JavaScript常用脚本汇总(一)
Mar 04 Javascript
jQuery实现点击查看大图并以弹框的形式居中
Aug 08 Javascript
原生JS取代一些JQuery方法的简单实现
Sep 20 Javascript
微信小程序图片宽100%显示并且不变形
Jun 21 Javascript
vue父组件向子组件(props)传递数据的方法
Jan 02 Javascript
Javascript的this详解
Mar 23 Javascript
javascript中的相等操作符(==与===区别)
Dec 21 Javascript
详解前端任务构建利器Gulp.js使用指南
Apr 30 Javascript
JavaScript 接口原理与用法实例详解
May 12 #Javascript
ES5新增数组的实现方法
May 12 #Javascript
JavaScript内置对象之Array的使用小结
May 12 #Javascript
详解ES6新增字符串扩张方法includes()、startsWith()、endsWith()
May 12 #Javascript
Vue 中如何将函数作为 props 传递给组件的实现代码
May 12 #Javascript
详解ES6数组方法find()、findIndex()的总结
May 12 #Javascript
JQuery表单元素取值赋值方法总结
May 12 #jQuery
You might like
Laravel 4.2 中队列服务(queue)使用感受
2014/10/30 PHP
smarty简单入门实例
2014/11/28 PHP
php类的定义与继承用法实例
2015/07/07 PHP
PHP基于phpqrcode生成带LOGO图像的二维码实例
2015/07/10 PHP
php array_reverse 以相反的顺序返回数组实例代码
2017/04/11 PHP
PHP+redis实现的购物车单例类示例
2019/02/02 PHP
ext 同步和异步示例代码
2009/09/18 Javascript
ExtJs中简单的登录界面制作方法
2010/08/19 Javascript
ajax更新数据后,jquery、jq失效问题
2011/03/16 Javascript
dreamweaver 8实现Jquery自动提示
2014/12/04 Javascript
JSON相关知识汇总
2015/07/03 Javascript
JavaScript动态改变div属性的实现方法
2015/07/22 Javascript
jQuery实现自动切换播放的经典滑动门效果
2015/09/12 Javascript
老生常谈JavaScript数组的用法
2016/06/10 Javascript
常用的javascript设计模式
2017/01/11 Javascript
[05:17]DOTA2睡衣妹卖萌求签名 CJ第二天全明星影像
2013/07/28 DOTA
[02:42]DOTA2城市挑战赛收官在即 四强之争风起云涌
2018/06/05 DOTA
详解Django框架中用context来解析模板的方法
2015/07/20 Python
Python循环语句之break与continue的用法
2015/10/14 Python
Python中取整的几种方法小结
2017/01/06 Python
使用Django和Python创建Json response的方法
2018/03/26 Python
Python小进度条显示代码
2019/03/05 Python
python的json中方法及jsonpath模块用法分析
2019/12/06 Python
pytorch查看通道数 维数 尺寸大小方式
2020/05/26 Python
基于python实现生成指定大小txt文档
2020/07/20 Python
英国最大的百货公司:Harrods
2016/08/18 全球购物
英国最大的电子产品和家电零售企业:Currys PC World
2016/09/24 全球购物
Expedia丹麦:全球领先的旅游网站
2018/03/18 全球购物
塑料制成的可水洗的编织平底鞋和鞋子:Rothy’s
2018/09/16 全球购物
德国汽车零件和汽车配件网上商店:kfzteile24
2018/11/14 全球购物
促销活动计划书
2014/05/02 职场文书
小学领导班子对照材料
2014/08/23 职场文书
房屋租赁协议书
2014/10/18 职场文书
幼儿园教师工作总结2015
2015/04/02 职场文书
2015年度残疾人工作总结
2015/05/14 职场文书
年终工作总结范文
2019/06/20 职场文书