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 相关文章推荐
Extjs 几个方法的讨论
Jan 28 Javascript
Javascript 实用小技巧
Apr 07 Javascript
JQuery 技巧和窍门整理(8个)
Apr 22 Javascript
IFrame跨域高度自适应实现代码
Aug 16 Javascript
js 控制图片大小核心讲解
Oct 09 Javascript
JQuery解析HTML、JSON和XML实例详解
Mar 29 Javascript
js实现网页右上角滑出会自动消失大幅广告的方法
Feb 27 Javascript
JavaScript 对象字面量讲解
Jun 06 Javascript
js显示动态时间的方法详解
Aug 20 Javascript
利用原生JS自动生成文章标题树的实例
Aug 22 Javascript
JS中的算法与数据结构之栈(Stack)实例详解
Aug 20 Javascript
jquery实现轮播图特效
Apr 12 jQuery
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
《神奇女侠:血脉》神力女超人大战犯罪公司
2020/04/09 欧美动漫
php_screw 1.5:php加密: 安装与使用详解
2013/06/20 PHP
PHP实现的DES加密解密实例代码
2016/04/06 PHP
PHP的反射机制实例详解
2017/03/29 PHP
浅谈PHP中的面向对象OOP中的魔术方法
2017/06/12 PHP
PHP MVC框架中类的自动加载机制实例分析
2019/09/18 PHP
『JavaScript』限制Input只能输入数字实现思路及代码
2013/04/22 Javascript
JS获取屏幕,浏览器窗口大小,网页高度宽度(实现代码)
2013/12/17 Javascript
基于jQuery的判断iPad、iPhone、Android是横屏还是竖屏的代码
2014/05/11 Javascript
浅谈JavaScript Array对象
2014/12/29 Javascript
angularjs学习笔记之简单介绍
2015/09/26 Javascript
JavaScript获取各大浏览器信息图示
2015/11/20 Javascript
浅谈javascript中new操作符的原理
2016/06/07 Javascript
JS中页面与页面之间超链接跳转中文乱码问题的解决办法
2016/12/15 Javascript
用js将long型数据转换成date型或datetime型的实例
2017/07/03 Javascript
详解http访问解析流程原理
2017/10/18 Javascript
微信小程序获取手机系统信息的方法【附源码下载】
2017/12/07 Javascript
动态加载、移除js/css文件的示例代码
2018/03/20 Javascript
使用Vant完成Dialog弹框案例
2020/11/11 Javascript
[00:59]DOTA2背景故事第二期之四大基本法则
2020/07/07 DOTA
Linux 下 Python 实现按任意键退出的实现方法
2016/09/25 Python
python针对excel的操作技巧
2018/03/13 Python
THE OUTNET美国官网:国际设计师品牌折扣网站
2017/03/07 全球购物
教师专业理论水平的自我评价分享
2013/11/09 职场文书
电子商务专业学生的自我鉴定
2013/11/28 职场文书
计算机网络工程专业职业生涯规划书
2014/03/10 职场文书
幼儿园教研活动总结
2014/04/30 职场文书
小学国庆节活动方案策划书
2014/09/16 职场文书
乡镇务虚会发言材料
2014/10/20 职场文书
2014年销售经理工作总结
2014/12/01 职场文书
2015出纳试用期工作总结
2014/12/12 职场文书
销售会议开幕词
2015/01/28 职场文书
七一建党节慰问信
2015/02/14 职场文书
河童之夏观后感
2015/06/11 职场文书
2016年党员创先争优公开承诺书
2016/03/25 职场文书
如何书写先进事迹材料?
2019/07/02 职场文书