目录

  • 引用类型与对象
  • 类与对象
  • 成员组成
  • 成员访问
  • 实例方法 / 属性

引用类型与对象

JavaScript 存在着两种数据类型:“基本数据类型” 与 “引用数据类型”。

“引用数据类型” 是一种数据结构,它将数据(属性)与功能(方法)组织在一起,引用类型的值就是“引用类型”的实例。

var obj = new Object();

其中 Object 就是一种“引用类型”,而 obj 则是对应 Object 类型的值(实例),实例继承着对应引用类型的属性和方法。ECMAScript 还提供了很多其它的引用类型,例如:Array、Date、Math、Number、String、RegExp、Boolean、Function 等。

在 JavaScript 中一切皆对象,这是因为其它的对象类型都继承自 Object,当然也包括着上述的所有“引用类型”,因此 Object 在 JavaScript 中是一切对象的基础对象。更直白的说,Window,Location、JSON、XMLHttpRequest、Array,Date 等所有其它对象在原型链上都继承着 Object,都可以看作是对 Object 的扩展对象。

Location.__proto__.__proto__
Array.__proto__.__proto__
JSON.__proto__ /*
{
constructor : ƒ Object()
hasOwnProperty:ƒ hasOwnProperty()
isPrototypeOf:ƒ isPrototypeOf()
propertyIsEnumerable:ƒ propertyIsEnumerable()
toLocaleString:ƒ toLocaleString()
toString:ƒ toString()
valueOf:ƒ valueOf()
get __proto__:ƒ __proto__()
set __proto__:ƒ __proto__()
}
*/

Object 在 JavaScript 中有局部与全局两种含义,全局层面 JavaScript 中一切皆对象,都是继承至 OBJECT 都是 OBJECT 的扩展,不论是 Object、Location、Array、JSON ... 本质上都是 OBJECT。而作为局部的含义 Object 就是单纯指 Object 这个对象,既通过 Object() 这个构造方法实例得到的键值对的无序组合体。

对于 “引用数据类型” 和“对象”关系,“引用类型” 必然是对象,但是对象不一定就是“引用数据类型”,因此 Location、XMLHttpRequest 等虽然是对象,但并不是一种“引用数据类型”。

虽然在 ECMAScript 中 Object 的实例并不具备多少功能,但对于应用程序中数据的存储与传输而言,则是非常理想的选择。

类与对象

JavaScript 中的“对象”与 Java 这样面向对象语言中的“类”,还是有很大的区别,简要可以概括出三点:

创建

Java中的对象必须由“类”来实例化,而 JavaScript 中对象可以由字面量格式非常简单的定义与创建,在 JavaScript 中对象就是很单纯的数据与功能的集合。

调用

Java 中类是不可以直接调用的,只是用来实例化对象,就算是调用方法也只能调用类上的静态方法,而在 JavaScript 中 充当类的构造函数是可以作为函数来调用的,例如:

Object(); //{}
String(); //""
Number(); //0

另外构造器方法上的静态方法也可以直接使用,例如:String.fromCharCode(65)。因此可以看出 JS 对函数式编程支持更好!

继承

在面向对象编程语言中 “类” 的重要概念就是继承,由“类”实例化的对象便会继承该类的方法与属性,这种继承关系更类似于派生以及父子关系,而在 JS 中继承被更多的看作成具有相互关联的关系,而这个关系的联接就是我们常说的“原型链(proto)”。

var Behavior = {
eat: () => {
alert('eat')
}
}; var Tom = { name: 'my name is Tom', __proto__: Behavior };
var Bob = { name: 'my name is Bob', __proto__: Behavior }; Tom.name; // my name is Tom;
Tom.eat();
Bob.eat();

当然 JS 也支持面向对象开发语言的 new 运算符,例如常见的:

var obj = new Object();

但实际上这个 new 运算符更多的充当着语法糖角色,大致上来说,new 运算符主要有以下功能:

  • 新建并返回一个构造器函数的实例对象,Constructor {} ,然后将构造函数中的 this 对象复制到新建的实例对象 this 上。
  • 把构造函数的 prototype 对象复制到新建对象的 __proto__ 属性上,以便继承该构造器 prototype 对象上的属性与方法,例如:
    var obj = new Object();
obj.__proto__ === Object.prototype; // true;

实例化得到的 obj 对象其 __proto__ 属性所指向关联(继承)的就是 Object() 构造器的 prototype 属性(原型属性)。

再比如一个完整的例子:

function Student(name) {
this.name = name;
} Student.prototype.say = function() { //将实例对象的 __proto__ 与自身的 prototype 进行了关联。
alert(this.name); //复制了this到实例对象上
} var Tom = new Student('Tom'); Tom.say(); //Tome

此时 new 运算符做了两件事,第一把 Student 中的 this 复制到新的实例对象 Tom 上所以 Tom 上也存在 name 属性,并且值就是实例化过程中传入的 Tom。第二则是把 Studentprorotype 对象复制到 Tom 这个实例化对象的 __proto__ 属性上。

console.log(Student.prototype);
console.log(Tom.__proto__); /*
{
say: ƒ()
constructor: ƒ Student(name)
__proto__: Object
}
*/

从上例可以看出 Tom.__proto__Student.prototype 指向的都是同一个原型对象,所以就非常好理解通过 new 运算符是如何实现继承关系的了。

下面是该原型对象上的具体属性与方法:

  • say : 原型对象上的方法,可以被其实例对象继承。
  • constructor : 用于说明当前的原型对象属于那个构造函数,同时也是为了实现一个闭环的循环访问(构造函数通过 prototype 访问原型对象,原型对象再通过 constructor 属性访问所依附的构造函数 )。
  • proto : 该原型对象(prototype)上的原型链属性(用于说明该原型对象自身的继承关系)。

一般来说实例对象的 __proto__ 属性保存的是其构造函数的 prototype 原型对象,而构造函数的原型对象本身也通过 __proto__ 来说明其自身的继承关系,同样的,原型对象的继承对象也含有 __proto__ 属性,依次类推我们便可以得出一个 __proto__ 属性串联出的 “继承链—>原型链”。

以一个实例的数组对象为例:

Array()
length: 0
__proto__: Array(0){ map:ƒ map()
filter:ƒ filter()
push:ƒ push()
indexOf:ƒ indexOf()
sort:ƒ sort()
...
constructor:ƒ Array()
__proto__:Object{
constructor:ƒ Object()
hasOwnProperty:ƒ hasOwnProperty()
isPrototypeOf:ƒ isPrototypeOf()
...
__proto__ : null
}
}

从中我们可以看出 JavaScript Object 对象是一切其它对象的最底层最基础的对象,其它对象都是继承自 Object,而 Object 继承的则是 null。

从示例中 Array() 方法产生的匿名实例数组,其 __proto__ 属性指向的是 Array 构造函数的 prototype 对象,而 Array 构造函数的 prototype 对象又继承自 Object() 构造函数的 prototype 原型对象。所以这种链式的继承关系可以如下图所示:

目前我们可以简单的总结下:在 JS 中构造函数类似与面向对象中“类”的关键点就在于方法(函数)具有 prototype 原型对象,它可以作为一个方法与属性的公共载体,用于该构造函数的实例对象通过 __proto__ 属性进行继承,并且其本身也通过一个 constructor 属性再循环访问到自身所依附的构造函数。由于普通对象不存在 prototype 对象,所以也就无法充当“类”的角色,但是对象自身 __proto__ 属性天生就是用来主动继承的,所以通过手动修改对象的 __proto__ 属性也可以很灵活的调整对象与对象之间的继承关系。

成员组成

前面提到过 Object 类型的实例是数据与方法的集合,由无序的键值对(key/value)构成,因此对其中每条 key/value 就可以看做成是这个对象实例的成员,而 key 则是对象的属性名或方法名,value 则是对象的属性值或具体的功能方法。

在 ECMAScript 中 key 名可以由任何属于 Unicode 编码的字符组成,但常用的还是数字与字母。

成员访问

在 ECMAScript 中成员的访问主要有两种方式:

  • "点" 表示法
  • "[]" 方括号表示法

它们的区别是 “点” 表示法更通用,更直观,更快捷。但缺点则是无法访问含有特殊字符、关键字、保留字的成员名。

Person.first name // Syntax Error

此时,括号表示法的强大就体现了出来。

Person ['first name'];
Person ['first' + 'name'];

实例方法 / 属性

constructor

保存着创建当前实例对象的构造函数。

new Object().constructor; // "ƒ Object() { [native code] }"

这说明当前对象的构造函数就是 Object()。

hasOwnProperty()

接收一个参数 key ,判断这个成员是否为实例对象私有的方法与属性,而非继承着原型。

function Test() {}
Test.prototype.custom = 1; var test = new Test(); test.custom_private = 1; console.log(test.hasOwnProperty('custom')); // false;
console.log(test.hasOwnProperty('custom_private')); // true;

isPrototypeOf()

接收一个对象作为参数,判断这个对象是否继承于指定的原型对象。

Test.prototype.isPrototypeOf(test) // true

propertyIsEnumerable()

接收一个参数 key,判断这个成员在指定的对象上是否可枚举(遍历访问)。

console.log(test.propertyIsEnumerable('custom')); // false;
console.log(test.propertyIsEnumerable('custom_private')); // true;

toLocaleString()

把对象转换为符合本地区格式的字符串。

toString()

把对象转换为字符串。

test.toString(); //  "[object Object]"

在 JavaScript 中也只有 Object 的 toString() 方法才会返回这种格式,因此利用这个特性,我们可以在转换格式统一的基础上进行类型的判断。

Object.prototype.toString.call({})      //"[object Object]"
Object.prototype.toString.call([]) //"[object Array]"
Object.prototype.toString.call("") //"[object String]"
Object.prototype.toString.call(1) //"[object Number]"
Object.prototype.toString.call(true) //"[object Boolean]"
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call(undefined)//"[object Object]"

valueOf()

返回对象自身。

JavaScript: 认识 Object、原型、原型链与继承。的更多相关文章

  1. JavaScript原型链与继承

    最近学习了<Javascript高级程序设计>面向对象部分,结合书中的例子总结一下原型链和继承部分的内容. 创建对象 在Js当中没有类这个概念,当我们想要创建具有相同属性的对象的时候,有如 ...

  2. 《JAVASCRIPT高级程序设计》根植于原型链的继承

    继承是面向对象的语言中,一个最为津津乐道并乐此不疲的话题之一.JAVASCRIPT中的继承,主要是依靠原型链来实现的.上一篇文章介绍过,JAVASCRIPT中,每一个对象都有一个prototype属性 ...

  3. 对Javascript 类、原型链、继承的理解

    一.序言   和其他面向对象的语言(如Java)不同,Javascript语言对类的实现和继承的实现没有标准的定义,而是将这些交给了程序员,让程序员更加灵活地(当然刚开始也更加头疼)去定义类,实现继承 ...

  4. JavaScript原型链及继承

    在JavaScript中,所有的东西都是对象,但是JavaScript中的面向对象并不是面向类,而是面向原型的,这是与C++.Java等面向对象语言的区别,比较容易混淆,因此把我自己学习的过程记录下来 ...

  5. JavaScript中的原型链和继承

    理解原型链 在 JavaScript 的世界中,函数是一等公民. 上面这句话在很多地方都看到过.用我自己的话来理解就是:函数既当爹又当妈."当爹"是因为我们用函数去处理各种&quo ...

  6. JavaScript原型链和继承

    1.概念 JavaScript并不提供一个class的实现,在ES6中提供class关键字,但是这个只是一个语法糖,JavaScript仍然是基于原型的.JavaScript只有一种结构:对象.每个对 ...

  7. 三张图搞懂JavaScript的原型对象与原型链 / js继承,各种继承的优缺点(原型链继承,组合继承,寄生组合继承)

    摘自:https://www.cnblogs.com/shuiyi/p/5305435.html 对于新人来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与__pro ...

  8. javascript精髓篇之原型链维护和继承.

    一.两个原型 很多人都知道javascript是原型继承,每个构造函数都有一个prototype成员,通过它就可以把javascript的继承演义的美轮美奂了. 其实啊,光靠这一个属性是无法完成jav ...

  9. [转]深入javascript——原型链和继承

    在上一篇post中,介绍了原型的概念,了解到在javascript中构造函数.原型对象.实例三个好基友之间的关系:每一个构造函数都有一个“守护神”——原型对象,原型对象心里面也存着一个构造函数的“位置 ...

随机推荐

  1. new和delete

    和 sizeof 类似,sizeof不是函数,它是一个操作符,它在编译期就完成了计算,在函数运行期间它已经是一个常数值了. int a; sizeof(int) = 4; sizeof(a) = 4; ...

  2. android数据库简单操作

    1.DbOpenHelper package com.example.dbtest.dbHelper; import android.content.Context; import android.d ...

  3. 进度条QProgressBar

    import sys from PyQt5.QtCore import Qt, QTimer from PyQt5.QtWidgets import QApplication, QWidget, QP ...

  4. adb shell dumpsys meminfo [packagename] 输出内容的含义

    Private Dirty:私有的脏内存页(还在使用中)的大小:   Private Clean:私有的干净内存页(现在未使用了)的大小: 以上这二者相加,便是应用曾经申请过的内存空间大小.Priva ...

  5. Android勒索软件研究报告

    Android勒索软件研究报告 Author:360移动安全团队 0x00 摘要 手机勒索软件是一种通过锁住用户移动设备,使用户无法正常使用设备,并以此胁迫用户支付解锁费用的恶意软件.其表现为手机触摸 ...

  6. 2018-2019-2 网络对抗技术 20165227 Exp2 后门原理与实践

    2018-2019-2 网络对抗技术 20165227 Exp2 后门原理与实践 (1)例举你能想到的一个后门进入到你系统中的可能方式? 接收邮件的方式 (2)例举你知道的后门如何启动起来(win及l ...

  7. AutoMapper中用户自定义转换

    Custom Type Converters Sometimes, you need to take complete control over the conversion of one type ...

  8. Pycharm 字体大小调整

    Pycharm 字体大小调整 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/awyyauqpmy/article/details/79334496P ...

  9. Python中的exec、eval使用实例

    Python中的exec.eval使用实例 这篇文章主要介绍了Python中的exec.eval使用实例,本文以简洁的方式总结了Python中的exec.eval作用,并给出实例,需要的朋友可以参考下 ...

  10. Java中利用Scanner键入的字符串与其他字符串的比较

    利用Scanner获取到键入的字符串与其他字符串作比较时,如果直接用关系运算符 == 比较,得到的结果总是false,因为实际比较的是两个变量引用的内存地址: 而要比较其内容是否相等,可以使用Obje ...