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 相关文章推荐
JS对URL字符串进行编码/解码分析
Oct 25 Javascript
jQuery UI-Draggable 参数集合
Jan 10 Javascript
仿百度联盟对联广告实现代码
Aug 30 Javascript
Node.js实现的简易网页抓取功能示例
Dec 05 Javascript
js获取会话框prompt的返回值的方法
Jan 10 Javascript
基于javascript实现简单计算器功能
Jan 03 Javascript
Bootstrap零基础入门教程(三)
Jul 18 Javascript
jQuery 更改checkbox的状态,无效的解决方法
Jul 22 Javascript
前端js实现文件的断点续传 后端PHP文件接收
Oct 14 Javascript
js正则取值的结果数组调试方法
Oct 10 Javascript
创建Vue项目以及引入Iview的方法示例
Dec 03 Javascript
如何在vue-cli中使用css-loader实现css module
Jan 07 Vue.js
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
php 表单数据的获取代码
2009/03/10 PHP
PHP下escape解码函数的实现方法
2010/08/08 PHP
PHP json_encode() 函数详解及中文乱码问题
2015/11/05 PHP
PHP针对字符串开头和结尾的判断方法
2016/07/11 PHP
PHP中如何使用Redis接管文件存储Session详解
2018/11/28 PHP
PHP抽象类基本用法示例
2018/12/28 PHP
PHP文件后缀不强制为.php方法
2019/03/31 PHP
巧妙破除网页右键禁用的十大绝招
2006/08/12 Javascript
Javascript学习笔记9 prototype封装继承
2010/01/11 Javascript
javascript SpiderMonkey中的函数序列化如何进行
2012/12/05 Javascript
IE6下opacity与JQuery的奇妙结合
2013/03/01 Javascript
html文本框提示效果的示例代码
2014/06/28 Javascript
javascript获取select值的方法分析
2015/07/02 Javascript
javascript比较语义化版本号的实现代码
2016/09/09 Javascript
原生javascript移动端滑动banner效果
2017/03/10 Javascript
nodejs入门教程五:连接数据库的方法分析
2017/04/24 NodeJs
js 倒计时(高效率服务器时间同步)
2017/09/12 Javascript
webpack+vue-cli项目中引入外部非模块格式js的方法
2018/09/28 Javascript
JavaScript实现获取两个排序数组的中位数算法示例
2019/02/26 Javascript
[01:00:54]TI4正赛第二日开场
2014/07/20 DOTA
Python 2.7.x 和 3.x 版本的重要区别小结
2014/11/28 Python
Python常见异常分类与处理方法
2017/06/04 Python
python实现二叉树的遍历
2017/12/11 Python
使用python os模块复制文件到指定文件夹的方法
2019/08/22 Python
python中的itertools的使用详解
2020/01/13 Python
python词云库wordCloud使用方法详解(解决中文乱码)
2020/02/17 Python
python读写文件write和flush的实现方式
2020/02/21 Python
理肤泉俄罗斯官网:La Roche-Posay俄罗斯
2018/07/24 全球购物
函授本科自我鉴定
2013/11/03 职场文书
致接力运动员广播稿
2014/02/17 职场文书
个人诉讼委托书范本
2014/10/17 职场文书
酒店财务总监岗位职责
2015/04/03 职场文书
2016年幼儿园教师师德承诺书
2016/03/25 职场文书
简历中的自我评价怎么写呢?
2019/04/30 职场文书
mysql死锁和分库分表问题详解
2021/04/16 MySQL
分析并发编程之LongAdder原理
2021/06/29 Java/Android