javascript中面向对象中对象,属性,原形链和一些扩展知识总结
javascript中面向对象中对象,属性,原型链和一些扩展知识总结
扩展知识:
json对象和json字符串之间的转换:
json对象和json协议之间的区别:
② 用构造函数实现的对象
a.在内存中创建了一个空的对象,就像是这样 var p = {};
b.拷贝对象中的属性和方法到空对象中。
c.自动生成一个__proto__属性指向类的原型。
有关c我们可以通过如下来验证。
hasOwnProperty:用来判断某个方法或者属性是否属于该对象,备注:只会去寻找对象构造函数或者json对象里面的属性和方法,还用person举例:
instanceof : mdn 上的api详解:
简要的说:instanceof运算符可以用来判断某个构造函数的prototype属性所指向的對象是否存在于另外一个要检测对象的原型链上。
isPrototypeOf() 方法测试一个对象是否存在于另一个对象的原型链上。
语法:object instanceof constructor
isPrototypeOf 方法允许你检查一个对象是否存在于另一个对象的原型链上。
语法:prototype.isPrototypeOf(object)

面向对象和面向对象编程
面向对象
就是找个工具,帮我完成一项工作,对象就像一个工具一样,每个工具都可以帮我们实现某个功能,比如汽车可以实现运输,我们只需要学会如何去开动汽车,而不需要知道汽车是如何实现开动的。面向对象的好处:我们所有的项目都是团队作战。通过对象实现任务分离。用对象的某一部分实现一项功能,可以实现团队作战。对象实质上就是一个包含多个工具的工具包,用对象来实现对功能的分类管理。
比如一个字符串对象,有toString,substring,subStr,indexOf,lastIndexOf等函数,用来实现不同的功能。
在对象中可以包含属性,方法,通过点语法来访问对象的属性和方法。
面向对象编程
相当于组装一辆汽车。汽车是一个对象,我们需要让汽车有移动,刹车等功能。我们先要定义这个对象,就是构造函数。然后给汽车添加轮胎,发动机等,就相当于添加属性,我们让汽车跑起来,刹车,就相当于添加方法。而一个刹车系统,相当于对象中的一个模块。我们通过模块化来管理整个对象。这就是面向对象编程。而在我们给别人使用这个对象的时候,只需要让别人实例化这个对象,然后就可以访问对象中的属性和对象中的方法了。
对象的几种不同表现形式
下面列举几个,更多请看我之前的博客:http://blog.****.net/tyro_java/article/details/51031006
① json对象
var person = {"name":"tom","age":"10"}此处用json实现一个person对象,json对象是对象的一种字面量表示形式
扩展知识:
json对象和json字符串之间的转换:
通过JSON.parse(str),把字符串转换成json对象,通过JSON.stringify(obj)把对象转换成字符串。
json对象和json协议之间的区别:
json协议是一种国际标准,用于前端与后端进行交换数据的格式。是一种规范,按照这种规范就能实现数据的传递。json对象是对象的字面量表示形式,相当于一个实例化后的对象。
② 用构造函数实现的对象
function Person (name,age) { this.name = name; this.age = age; } var person = new Person('Tom',10); // 实例化对象 console.log(person.name); // 此处我们通过点语法来访问对象的属性 console.log(person.age);
③ 通过构造函数和原型来实现对象
function Person (name,age) { this.name = name; this.age = age; } Person.prototype = { say:function() { console.log(this.name + ' is saying'); } } var person = new Person('Tom',10); person.say(); // Tom is saying扩展知识:通常,我们都用此种方法来实例化对象,为了节省内存,我们把对象的方法都放在原型里面。为什么呢? 在我们通过new实例化对象的时候,在内存中,会自动拷贝构造函数中的所有属性和方法,用来给实例对象赋值,而不管我们实例化多少次,原型里面的属性和方法只生成一次,所以会节省内存。我们在实例化new的时候,系统做了如下几件事情:
a.在内存中创建了一个空的对象,就像是这样 var p = {};
b.拷贝对象中的属性和方法到空对象中。
c.自动生成一个__proto__属性指向类的原型。
有关c我们可以通过如下来验证。
console.log(person.__proto__ === Person.prototype); // true另外,由此引出两个方法 hasOwnProperty() 和 isPrototypeOf()方法
hasOwnProperty:用来判断某个方法或者属性是否属于该对象,备注:只会去寻找对象构造函数或者json对象里面的属性和方法,还用person举例:
// 用三个if来 测试 hasOwnProperty if(person.hasOwnProperty('name')){ console.log('name exsit'); // 输出 name exsit } if(person.hasOwnProperty('say')){ console.log('say exsit'); // 不输出 } if(Person.prototype.hasOwnProperty('say')){ console.log('say exsit'); // 输出 say exsit }hasOwnProperty 此方法不会检查该对象的原型链,被检查的属性和方法必须是对象本身的一个成员。
比如第二if,它不会检查原型里的say方法,只会在构造函数中去找,所以不输出。
比如第三个if中用Person.prototype中,有输出,是因为Person.perototype本身就是一个对象,它在自己里面能找到say方法。
isPrototypeOf:它是基类对象Object的一个方法:Object.prototype.isPrototypeOf()。
isPrototypeOf:它是基类对象Object的一个方法:Object.prototype.isPrototypeOf()。
先做个示例:obj1.isPrototypeOf(obj2),它的意思是obj1是否被包含在obj2的原型链中,下面是示例代码:
// 先创建4个构造函数 function A() {} function B() {} function C() {} function D() {} B.prototype = new A(); // 把B的原型指向A实例化后的对象 C.prototype = new B(); // 把C的原型指向B实例化后的对象 D.prototype = new C(); // 把D的原型指向C实例化后的对象 // 下面开始来测试 var d = new D(); if (B.prototype.isPrototypeOf(d)) { console.log('haha'); // 输出 haha }从这里可以看出 isPrototypeOf 和 instanceof 的区别,我们开始详细的说下:
instanceof : mdn 上的api详解:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/instanceof
isPrototypeOf : mdn 上的api详解:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/IsPrototypeOf
简要的说:instanceof运算符可以用来判断某个构造函数的prototype属性所指向的對象是否存在于另外一个要检测对象的原型链上。
isPrototypeOf() 方法测试一个对象是否存在于另一个对象的原型链上。
function A() {} function B() {} function C() {} function D() {} B.prototype = new A(); // 把B的原型指向A实例化后的对象 C.prototype = new B(); // 把C的原型指向B实例化后的对象 D.prototype = new C(); // 把D的原型指向C实例化后的对象 var d = new D(); var c = new C(); var b = new B(); var a = new A(); console.log(A.prototype.isPrototypeOf(d)); // true 这里A是处于顶层,表示 A.prototype处于d的原型链上 console.log(B.prototype.isPrototypeOf(d)); // true console.log(C.prototype.isPrototypeOf(d)); // true console.log(D.prototype.isPrototypeOf(d)); // true console.log(d instanceof C); // true console.log(d instanceof B); // true console.log(d instanceof A); // trueobject instanceof AFunction 检测的是AFunction.prototype是否在object的原型链中,而不是检测AFunction自身。
语法:object instanceof constructor
isPrototypeOf 方法允许你检查一个对象是否存在于另一个对象的原型链上。
语法:prototype.isPrototypeOf(object)
属性进阶
万物皆属性
方法从某种角度来说,也可以看成一个属性,方法的定义,使用,在内存中的存储,都基本一致。
function Test(){ this.name = 'test'; this.test = function() { console.log('test'); } }
万物皆变量
所有的数据都是通过变量来保存(管理)的,变量都是在内存中存储的
var str = 'javascript'; var json = {"name":"javascript"} var fn = function() {}扩展知识:函数声明和函数表达式的区别 函数声明会被提升,函数表达式不会被提升,请看下面代码:
function fn() {} var func = function(){};上面的代码实际解析的顺序是这样的:
var func; function fn(){}; func = function(){};有关提升的问题,请参考我之前的博文:http://blog.****.net/tyro_java/article/details/51131812
属性分类
公有属性、私有属性、公有静态属性、类方法、原型方法、原型属性
function Car(type,price) { this.type = type; // 公有属性 this.price = price; var private = 1; // private 是私有属性,外界或者原型里都无法访问,只在构造函数内部能访问 this.run = function() { // 对象方法 console.log('run'); } } Car.prototype = { color:'black',// 原型属性 drive:function() { // 原型方法 console.log('drive'); }, test:function() { console.log(private); } } Car.Weight = 1000; // 公有静态属性,只能通过构造函数来访问,首字母一般大写 Car.Fly = function(){ // 类方法 ,只能通过构造函数来访问,首字母一般大写 console.log('car can\'t fly'); }; // 调用测试 var c = new Car('small',10000); console.log(c.run()); // run console.log(c.color); // black console.log(Car.prototype.color); // black console.log(c.drive()); // drive console.log(Car.Weight); // 1000 console.log(Car.Fly()); // car can't flyget,set取值器和设置器,用于对某一属性进行包装,用于取值和设置,js原生自带的两种。因为这个比较复杂我找到了一篇api文档,详细说明了这个,还有 Object.defineProperty() 方法的介绍。
文档如下:https://msdn.microsoft.com/zh-cn/library/dd548687
对象实例进阶
在进行这个话题之前,我们需要了解一下,实例化后的对象是如何在内存中存储的。
当我们实例化的时候,会在内存里面开辟两段区域:栈和堆。
栈专门存储简单变量,堆专门用来存储对象。
当我们实例化的时候,会在内存中开辟一段区域,然后自动拷贝构造函数中所有的属性和方法。
虽然都是通过同一个对象new出来的,但是不同的实例,会被分配到不同的空间,当我们更改一个实例的属性时,不会影响到另一个实例。
但是有一点,如果你更改的是在原型里的引用类型的属性,那么这个引用类型属性时共享的,更改一个,会影响其他实例。
原因是原型里面的引用类型属性是全部共享的,值类型不会共享,举例如下:
function Classroom(id,num,teacher) { this.id = id; this.num = num; this.teacher = teacher; } Classroom.prototype = { property:{ blackbord:"only one", chalk:"110pieces" }, other:'hehe' } var cr = new Classroom(3,2,'Tom'); cr.property.blackbord = 'has two'; // 修改引用类型的property属性 cr.other = 'crhehe'; // 修改值类型的other属性 var cr2 = new Classroom(1,2,'Jack'); console.log(cr2.property.blackbord); // has two ,这里其它实例受到影响了 console.log(cr2.other); // hehe, 这里其它实例不受影响。每个实例对象都有两个隐藏的属性一个是__proto__,一个是constructor。实例的__proto__属性来自它的构造函数,实例的constructor来自它的构造函数的原型里,下面用浏览器的 repl 环境下测试来验证这一说法,截图验证:
原型链进阶
原型
在js中只要是一个对象都会有一个原型,也就是prototype,那么js设计者为什么会这样设计呢,因为这样设计有个好处,就是节省内存。那么,我们再次来说下,实例化一个对象的时候,系统都做了哪些事情:
1.创建一个空的对象
2.拷贝构造函数中的属性和方法放到空对象里
3.自动生成一个__proto__属性指向类的原型
因为对象的方法中大都是大片的代码块,如果都放在构造函数里面就会有大量的内存开支,因为每次我们new的时候,都会重复上面三个步骤,而prototype里的东西只在第一次new的时候生成,不会每次都生成,所以我们需要把那些比较占用内存的方法放到原型里面。
原型对象的特性
1.不管实例化多少次,原型对象只生成一次。
2.原型对象中的属性和方法可以被所有实例访问。
3.原型对象中引用类型的属性是共享的,值类型的属性是各自拥有的。
(在我之前的博文中有提到: http://blog.****.net/tyro_java/article/details/50990135 ,看实践二和实践三)
4.对象实例化后实际上生成了两个对象,一个是构造函数对象,一个是原型对象。
原型链
我们知道,类的构造函数和类的原型(prototype)之间有一个联系,就是类的构造函数中的一个隐藏属性__proto__ ,它指向了这个类的prototype。
而类的prototype里面也有一个隐藏属性constructor,指向了这个类的构造函数。
另外类的prototype里面还有一个隐藏属性__proto__ 指向了这个类的父类,由此实现了链条关系。
还在浏览器的repl环境里举个例子:
内置对象的原型链
分析:上面str是一个String对象的一个实例,实例有两个隐藏属性 __proto__ 和constructor
str.constructor === String.prototype.constructor 为true表示,实例的隐藏属性constructor 来自类的原型。
str.__proto__ === String.prototype 为true ,表示实例的__proto__指向类的原型
str.constructor === String 和 String.prototype.constructor === String 都为true 表示constructor指向类的构造函数
str.__proto__ === String.prototype 为true ,表示实例的__proto__指向类的原型
str.constructor === String 和 String.prototype.constructor === String 都为true 表示constructor指向类的构造函数
String.prototype.__proto__ === Object.prototype 为true 表示了继承关系,String类原型中的__proto__指向了父类Object的原型
属性和方法的搜索机制
实例化后的对象首先遍历自身的属性和方法,如果没有找到,就通过类构造函数中的__proto__去类的原型里去找,如果在类原型里面还没有找到,那么按着类的原型中的__proto__这个链条到上一级的类的原型中找,一直按着这种关系,直到搜索到Object.prototype,如果还没找到返回undefined,在系统中最终的链条是null,Object.prototype.__proto__
=== null ,所以说null 是JavaScript面向对象的源头。
阶段性总结一下:js通过__proto__ 这个链条将构造函数和原型以及一层一层的原型直到最终的null联系起来,这就是所谓的原型链。
阶段性总结一下:js通过__proto__ 这个链条将构造函数和原型以及一层一层的原型直到最终的null联系起来,这就是所谓的原型链。
① Object对象的__proto__ :Object对象是Function对象的一个实例 如下所示:
console.log(Object.constructor === Function); // true console.log(Object.__proto__ === Function.prototype); // true②不止是Object,在js中所有的内置对象都是Function的一个实例,如下所示:
console.log(String.constructor === Function); // true console.log(String.__proto__ === Function.prototype); // true console.log(Array.constructor === Function); // true console.log(Array.__proto__ === Function.prototype); // true console.log(Image.constructor === Function); // true console.log(Image.__proto__ === Function.prototype); // true // ... and so on console.log(Number.__proto__ === Function.prototype); // true console.log(Boolean.__proto__ === Function.prototype); // true console.log(String.__proto__ === Function.prototype); // true console.log(Object.__proto__ === Function.prototype); // true console.log(Function.__proto__ === Function.prototype); // true console.log(Array.__proto__ === Function.prototype); // true console.log(RegExp.__proto__ === Function.prototype); // true console.log(Error.__proto__ === Function.prototype); // true console.log(Date.__proto__ === Function.prototype); // true console.log(Image.__proto__ === Function.prototype); // true③ 而内置对象的实例,则各自指向各自的原型,如下所示:
var str = new String('hello'); var arr = new Array(1,2,3); var img = new Image(); console.log(str.__proto__ === String.prototype); // true; console.log(arr.__proto__ === Array.prototype); // true; console.log(img.__proto__ === Image.prototype); // true;④ 将随便挑一个比如String对象把它单独拿出来看,会有这么一条关系链,如下:
var str = new String('hello'); console.log(str.__proto__ === String.prototype); // true console.log(String.prototype.__proto__ === Object.prototype); // true console.log(String.__proto__ === Function.prototype); // true console.log(Function.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__ === null); // true
自定义对象的原型链
function A() {}; var a = new A(); console.log(a.__proto__ === A.prototype); // true console.log(A.constructor === Function); // true console.log(A.__proto__ === Function.prototype); // true console.log(Function.constructor === Function); // true console.log(Function.__proto__ === Function.prototype); // true console.log(Function.prototype.__proto__ === Object.prototype); // true console.log(A.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__ === null); // true
总结:null是链条的最终的源头。
Object.prototype是一切链式查找的最后一站。
Function.prototype是一切内置函数和自定义构造函数的原型对象。
而属性搜索机制的底层就是通过__proto__属性链接起来的。所以__proto__才是面向对象的底层实现机制,是理解面向对象本质所在。