JavaScript: 认识 Object、原型、原型链与继承。
目录
- 引用类型与对象
- 类与对象
- 成员组成
- 成员访问
- 实例方法 / 属性
引用类型与对象
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
。第二则是把 Student
的 prorotype
对象复制到 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、原型、原型链与继承。的更多相关文章
- JavaScript原型链与继承
最近学习了<Javascript高级程序设计>面向对象部分,结合书中的例子总结一下原型链和继承部分的内容. 创建对象 在Js当中没有类这个概念,当我们想要创建具有相同属性的对象的时候,有如 ...
- 《JAVASCRIPT高级程序设计》根植于原型链的继承
继承是面向对象的语言中,一个最为津津乐道并乐此不疲的话题之一.JAVASCRIPT中的继承,主要是依靠原型链来实现的.上一篇文章介绍过,JAVASCRIPT中,每一个对象都有一个prototype属性 ...
- 对Javascript 类、原型链、继承的理解
一.序言 和其他面向对象的语言(如Java)不同,Javascript语言对类的实现和继承的实现没有标准的定义,而是将这些交给了程序员,让程序员更加灵活地(当然刚开始也更加头疼)去定义类,实现继承 ...
- JavaScript原型链及继承
在JavaScript中,所有的东西都是对象,但是JavaScript中的面向对象并不是面向类,而是面向原型的,这是与C++.Java等面向对象语言的区别,比较容易混淆,因此把我自己学习的过程记录下来 ...
- JavaScript中的原型链和继承
理解原型链 在 JavaScript 的世界中,函数是一等公民. 上面这句话在很多地方都看到过.用我自己的话来理解就是:函数既当爹又当妈."当爹"是因为我们用函数去处理各种&quo ...
- JavaScript原型链和继承
1.概念 JavaScript并不提供一个class的实现,在ES6中提供class关键字,但是这个只是一个语法糖,JavaScript仍然是基于原型的.JavaScript只有一种结构:对象.每个对 ...
- 三张图搞懂JavaScript的原型对象与原型链 / js继承,各种继承的优缺点(原型链继承,组合继承,寄生组合继承)
摘自:https://www.cnblogs.com/shuiyi/p/5305435.html 对于新人来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与__pro ...
- javascript精髓篇之原型链维护和继承.
一.两个原型 很多人都知道javascript是原型继承,每个构造函数都有一个prototype成员,通过它就可以把javascript的继承演义的美轮美奂了. 其实啊,光靠这一个属性是无法完成jav ...
- [转]深入javascript——原型链和继承
在上一篇post中,介绍了原型的概念,了解到在javascript中构造函数.原型对象.实例三个好基友之间的关系:每一个构造函数都有一个“守护神”——原型对象,原型对象心里面也存着一个构造函数的“位置 ...
随机推荐
- springSession框架来实现sso单点登陆
介绍一下springsession这个框架,其实springsession框架默认的是使用redis来实现单点登陆的,但是不支持redis集群,这个框架的特点是无侵入的实现单点登陆,就是说我们之前获取 ...
- Hibernate SQL查询 addScalar()或addEntity()【转】
本文完全引用自: http://www.cnblogs.com/chenyixue/p/5601285.html Hibernate除了支持HQL查询外,还支持原生SQL查询. 对原 ...
- C语言指针详解(经典,非常详细)
前言:复杂类型说明 要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其 ...
- ThinkPHP 3.2 用户注册邮箱验证激活帐号
本文将结合实例,讲解如何使用PHP+Mysql完成注册帐号.发送激活邮件.验证激活帐号.处理URL链接过期的功能. 业务流程 1.用户提交注册信息. 2.写入数据库,此时帐号状态未激活. 3.将用户名 ...
- C - Little Jumper (三分)
题目链接:https://cn.vjudge.net/contest/281961#problem/C 题目大意:青蛙能从一个点跳到第三个点,如图,需要跳两次.问整个过程的最大起跳速度中的最小的. 具 ...
- jquery 学习(二) - 属性操作
html代码 <div class="n1" zdy="z1">AAA <p>1111111</p> <input t ...
- java中的基本数据类型一定存储在栈中吗?
首先说明,"java中的基本数据类型一定存储在栈中的吗?”这句话肯定是错误的. 下面让我们一起来分析一下原因: 基本数据类型是放在栈中还是放在堆中,这取决于基本类型在何处声明,下面对数据类型 ...
- <crtdbg.h> 的作用
1.在调试状态下让win程在输出窗口中显示调试信息,可以用_RPTn 宏n为显示参数比如_RPT0(_CRT_WARN,"text"); _RPT1(_CRT_WARN," ...
- dataTable插件锁表头和锁列的教程
源代码下载 我的同事让我帮忙给弄个锁头锁列的插件.结果找到大名鼎鼎的jquery dataTable插件. 今天我们来介绍不常用的功能:dataTable插件锁表头和锁前两列 由于是移动前端.我们不考 ...
- k64 datasheet学习笔记1---概述
1.前言 k64 datasheet描述了Freescale MCU的特性.架构和编程模型,主要是面向使用MCU的系统架构和软件应用开发人员. 2.模块划分 datasheet主要按功能对模块进行划分 ...