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 相关文章推荐
javascript中巧用“闭包”实现程序的暂停执行功能
Apr 04 Javascript
基于jquery的finkyUI插件与Ajax实现页面数据加载功能
Dec 03 Javascript
IE中图片的onload事件无效问题和解决方法
Jun 06 Javascript
JavaScript实现数字数组按照倒序排列的方法
Apr 06 Javascript
jQuery实现大转盘抽奖活动仿QQ音乐代码分享
Aug 21 Javascript
JavaScript中字符串与Unicode编码互相转换的实现方法
Dec 18 Javascript
详解Axios统一错误处理与后置
Sep 26 Javascript
angularJs中$scope数据序列化的实例
Sep 30 Javascript
vue插槽slot的理解和使用方法
Apr 03 Javascript
Webpack中loader打包各种文件的方法实例
Sep 03 Javascript
JS实现纵向轮播图(初级版)
Jan 18 Javascript
VUE+elementui组件在table-cell单元格中绘制微型echarts图
Apr 20 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
怎么样可以把 phpinfo()屏蔽掉?
2006/11/24 PHP
PHP中的integer类型使用分析
2010/07/27 PHP
PHP实现json_decode不转义中文的方法
2017/05/20 PHP
PHP标准库(PHP SPL)详解
2019/03/16 PHP
jQuery对下拉框,单选框,多选框的操作
2014/02/21 Javascript
js操作数组函数实例小结
2015/12/10 Javascript
nodeJs内存泄漏问题详解
2016/09/05 NodeJs
使用JavaScript获取Request中参数的值方法
2016/09/27 Javascript
canvas滤镜效果实现代码
2017/02/06 Javascript
JQuery Ajax 异步操作之动态添加节点功能
2017/05/24 jQuery
AngularJS 支付倒计时功能实现思路
2017/06/05 Javascript
python爬取安居客二手房网站数据(实例讲解)
2017/10/19 Javascript
初探js和简单隐藏效果的实例
2017/11/23 Javascript
JavaScript实现多重继承的方法分析
2018/01/09 Javascript
JavaScript实现写入文件到本地的方法【基于FileSaver.js插件】
2018/03/15 Javascript
angular6.x中ngTemplateOutlet指令的使用示例
2018/08/09 Javascript
详解webpack-dev-server使用方法
2018/09/14 Javascript
layui table单元格事件修改值的方法
2019/09/24 Javascript
layui 数据表格 根据值(1=业务,2=机构)显示中文名称示例
2019/10/26 Javascript
[02:37]2018DOTA2亚洲邀请赛赛前采访-EG篇
2018/04/03 DOTA
[01:00:53]OG vs IG 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
python批量修改文件名的实现代码
2014/09/01 Python
Python回文字符串及回文数字判定功能示例
2018/03/20 Python
django输出html内容的实例
2018/05/27 Python
python的pytest框架之命令行参数详解(上)
2019/06/27 Python
python适合人工智能的理由和优势
2019/06/28 Python
纯css3实现宠物小鸡实例代码
2018/10/08 HTML / CSS
webView加载html图片遇到的问题解决
2019/10/08 HTML / CSS
中国排名第一的外贸销售网站:LightInTheBox.com(兰亭集势)
2016/10/28 全球购物
司机岗位职责
2015/02/04 职场文书
Nginx中break与last的区别详析
2021/03/31 Servers
Python如何把不同类型数据的json序列化
2021/04/30 Python
Python利用folium实现地图可视化
2021/05/23 Python
python析构函数用法及注意事项
2021/06/22 Python
JS中如何优雅的使用async await详解
2021/10/05 Javascript
JVM之方法返回地址详解
2022/02/28 Java/Android