博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript: 认识 Object、原型、原型链与继承。
阅读量:5119 次
发布时间:2019-06-13

本文共 5987 字,大约阅读时间需要 19 分钟。

目录

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

引用类型与对象

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 原型对象。所以这种链式的继承关系可以如下图所示:

__proto__.png

目前我们可以简单的总结下:在 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()

返回对象自身。

转载于:https://www.cnblogs.com/HCJJ/p/10598120.html

你可能感兴趣的文章
嵌入式软件设计第8次实验报告
查看>>
算法和数据结构(三)
查看>>
Ubuntu下的eclipse安装subclipse遇到没有javahl的问题...(2天解决了)
查看>>
alter database databasename set single_user with rollback IMMEDIATE 不成功问题
查看>>
WCF揭秘——使用AJAX+WCF服务进行页面开发
查看>>
【题解】青蛙的约会
查看>>
IO流
查看>>
mybatis调用存储过程,获取返回的游标
查看>>
设计模式之装饰模式(结构型)
查看>>
面向对象的设计原则
查看>>
Swift3.0服务端开发(三) Mustache页面模板与日志记录
查看>>
【转】 FPGA设计的四种常用思想与技巧
查看>>
EntityFrameWork 实现实体类和DBContext分离在不同类库
查看>>
新手算法学习之路----二叉树(在一个二叉查找树中插入一个节点)
查看>>
autopep8
查看>>
GIT在Linux上的安装和使用简介
查看>>
基于C#编程语言的Mysql常用操作
查看>>
s3c2440实验---定时器
查看>>
MyEclipse10安装SVN插件
查看>>
[转]: 视图和表的区别和联系
查看>>