JavaScript基础入门教程(四)

说明

  前面三篇博客介绍了js中基本的知识点,包括变量类型及其转换、表达式、运算符等小知识点,这篇博客主要讲的是对象。如果你学过java等语言,你也许在下文的阅读中发现在js中的对象与java中的对象存在一定的区别。

相关术语说明

一、对象中成员变量的”属性特性“

  ①可写:表明该属性可以设置其值。

  ②可枚举:表明是否可以通过for/in循环返回该属性。

  ③可配置:表明是否可以删除或者修改该属性。

注:关于for/in等语句的说明,第一篇博文就说过,本系列教程是建立在读者对基本的编程有一定了解的基础上,所以一些基本的if、for等语句的语法在本系列教程中并不涉及,这样不仅能节约我的时间,也能节约大家阅读的时间。但是对于一些其他语言中少见的语法,在本系列教程中还是会谈到,这里虽然没有谈到for/in语句,但是已给出站外链接,供大家参考。总之鄙人认为有必要让读者了解的东西,要么会出现的自己的博文中,要么会给出站外链接。同样为了节约大家的时间,这里给出的链接一般是内容较好且行文精简的教程。

二、每个对象拥有的“对象特性”

  ①原型(prototype):指向另一个对象,js里面的继承就靠这个prototype实现。与一般的静态编程语言不同,js中的继承出现在对象上,而不是类上。

  ②类(class):标识对象类型的字符串。

  ③扩展标记(extensible flag):指明了该对象是否可以添加新属性。在js中对象的成员属性是可以动态添加的,而不像静态的面向对象的编程语言在编译前就确定了。

三、对象的分类

  ①内置对象:由JavaScript标准提供的对象或类,例如数组、正则等。

  ②宿主对象:由浏览器提供的对象,比如进行DOM操作时常用的document对象,还有进行BOM操作常用的window对象等。

  ③自定义对象:我们自己写的对象。

注:这里对一些属于的解释可能较为抽象,如果实在不明白也没关系,下面会用例子进行实例的讲解。

对象的创建

  js中对象的创建有三种方式,可以通过对象直接量、关键字new和Object.create()函数来创建。

原型

  在介绍对象的创建之前,有必要谈一下对象的原型对象。原型对象原本是属于一个函数的,但我们经常看到对象的原型对象这种说法。一般地,如果某一个函数是构造函数,由这个构造函数创建出来的对象的原型对象就是这个构造函数的原型对象。

  原型对象的出现实现了类似与继承的功能。一般来说每个对象的原型对象是不可访问的,但是在某些浏览器(比如chrome)中提供了对原型对象的访问,其属性名为__proto__。这么说起来你可能觉得比较抽象,难以理解,结合下面的图再讲你可能会更清楚。

  这张图中,Person是一个构造函数,p1和p2都是由这个构造函数创建出来的对象,它们三都指向同一个原型对象(Person prototype由解释器创建)。这里就相当于p1和p2继承于这个原型对象,它俩都共享原型对象的name和age属性,如果这个原型对象还有原型对象,它俩也可以共享,这样就能构造出一条原型链,处于原型链低端的对象可以访问原型链高处的成员属性。上图中p1有自己的name属性,所以它不会再向上访问原型对象的name。

  原型是js中理解对象编程的一个很重要的知识点,如果你觉得还是比较模糊,或者说想更清楚地了解有关原型的内容,强烈推荐你阅读这篇博客。另外上面这张图也是从这篇博客中复制过来的。

对象直接量

  这种创建对象的方式在前几篇的博客中也出现过,就是直接用一个大括号将属性/值括起来,其中属性名可以是任意的字符串,如果属性名是正常的标识符,则可以不用引号括起来,否则必须用引号括起来。属性名和其值之间用冒号分割开。不同的属性之间用逗号分开,具体看下面的例子。

 1 var empty = {};    //一个没有任何属性的对象。
 2 var point = {x:0, y:0};    //拥有两个属性的对象。
 3 var point2 = {x:point.x, y:point.y};
 4 var book = {
 5     "main title":"javaScript",
 6     ‘sub-title‘:"the definitive guide",
 7     "for":"all audiences",
 8     author:{
 9         firstname:"David",
10         lastname:"Flanagan"
11     }
12 };

  这里需要说明的就是point2和book引用的对象,其中point2引用的对象的属性值是一个表达式,js允许属性值是任意有值的表达式。在book中的main title和sub-title不是标识符,所以必须用引号括起来。for虽然是标识符但同时也是保留字,在ECMAScript3中必须用引号括起来,但是ECMAScript5中可以不括起来。值得注意的是对象直接量是一个表达式,这种表达式如果放在循环里面,每次被执行都会创建一个新的对象,且对象的属性值也有可能不同。

关键字new

  new运算符可以后跟一个构造函数来创建一个新对象。如果后面的构造函数没有参数传入,可以省略其后面的括号。

1 var o = new Object();    //没有参数传入,等价于new Object;
2 var a = new Array();    //和空数组[]一样。
3 var d = new Date();
4 var r = new RegExp("js");

  除了内置的构造函数,我们也可以自己定义构造函数来初始化新对象,这些内容将在后面的类和模块的博客中进行详细介绍。

Object.create()

  学过java的同学可能知道所有的类都有父类,除了Object类自己。在js中也差不多,所有的对象都有原型对象,除了null。利用Object.create这个静态函数创建对象可以手动指定该对象所继承的原型对象以及其属性。看下面的例子:

1 var o1 = Object.create({x:1, y:2});    //o1继承了x和y属性
2 var o2 = Object.create(null);    //不继承任何属性
3 var o3 = Object.create(Object.prototype);    //等价于{}或new Object()

  这里要注意的是o2这个对象,它的原型对象为null,所以它没有继承到任何属性,这和o3这个对象不同,o3这个对象虽然为{}空对象,但是它还是从它的原型对象那里继承了一些属性的,比如toString()方法等。上面说到的Object.create()还可以用第二个参数给新对象设置属性,这将在本博客的后面讲解。

属性的查询和设置

  js中对对象属性的访问有两种方式,分别利用点(.)和方括号([])进行。其中点运算符只支持对象属性访问,且属性名必须是标识符,而方括号不仅能访问对象的属性,还可以访问数组中的元素,且方括号里面的属性名可以是任意的字符串或者是能转换为字符串的表达式。

1 var author = book.author;    //等价于book[‘author‘]
2 var title = book[‘main title‘];    //只能用方括号访问。因为‘main title‘不是标识符,标识符不能含有空格。

  我们平时写的函数名、变量名等就是标识符,关于js中标识符的要求基本上和其它语言差不多,具体请参考本系列教程的第一篇中的”js的词法“部分。

  上面两个例子都是对对象中属性的查询访问,如果需要设置其属性只需将其放在赋值号的左边即可。如果是查询访问,当属性不存(也不存在于原型链中)在会返回undefined,如果是设置其属性,当属性不存在,会在当前对象中创建一个同名属性并设置其值。

  由于在属性访问时不存在的属性会返回undefined,所以这里涉及到一个错误处理的问题,见下面的代码:

 1 //存在问题的代码
 2 var len = book.subtitle.length;
 3
 4 //冗余但易懂的处理方法
 5 var len = undefined;
 6 if(book){
 7     if(book.subtitle)
 8         len = book.subtitle.length;
 9     }
10 }
11
12 //精简的常用方法
13 var len = book && book.subtitle && book.subtitle.length;

删除属性

  js中有一个delete运算符,用于删除对象的属性,但是它只能删除自有属性,不能删除继承而来的属性,要删除原型链上的属性必须找到那个原型。值得注意的是delete对属性的删除只是解除其指向这个属性值的引用,而不会去操作其属性的属性,因为js中所有内存的释放都是垃圾回收器完成的。见下面的代码:

1 var a={p:{x:1}};
2 var b=a.p;
3 delete a.p;    //解除了a.p指向{x:1}这个属性值的引用
4 b.x    //1:虽然a.p对{x:1}的引用没了,但是b对{x:1}的引用还在。

  一般情况下使用delete删除属性都将返回一个true,即使是以下这些情况也将返回true:

1 var o={x:1};
2 delete o.x;    //删除x返回true
3 delete o.x;    //什么也没做(x已经不存在了),返回true
4 delete o.toString;    //什么也没做(toString是继承来的),返回true。
5 delete 1;    //无意义,返回true

  上面说的一般情况,不过存在以下情况的delete语句会返回false。(注意以下情况是在非严格模式下,在严格模式下删除不可配置属性会报错。)

1 delete Object.prototype;    //不能删除,属性不可配置
2 var x=1;    //声明一个全局变量
3 delete this.x;    //全局变量的是不可配置的
4 function f() {}    //声明一个全局函数
5 delete this.f;    //也不能删除全局函数

检测属性

  属性的检测有三种方法,①通过in运算符、②通过hasOwnProperty()方法、③通过propertyIsEnumerable()方法。

in运算符

  in运算符左侧写属性名(字符串),右侧写对象名,如果这个对象含有或者继承了这个属性,则返回true。

1 var o = {x:1}
2 "x" in o;    //true
3 "y" in o;    //false
4 "toString" in o    //true

hasOwnProperty()方法

  只有这个属性是自有属性才返回true,对于继承而来的属性返回false。

1 var o = {x:1}
2 o.hasOwnProperty("x");    //true
3 o.hasOwnProperty("y");    //false
4 o.hasOwnProperty("toString");    //false

propertyIsEnumerable()方法

  与hasOwnProperty()方法相比更严格,要求该属性不仅是自有属性,还必须是可枚举的。(下面将讲解什么是可枚举)

其它

  除了以上三种方法用于判断某一对象是否包含某一属性外,还有一种方法如下:

1 var o={x:1}
2 o.x !== undefined;    //true
3 o.y !== undefined;    //false
4 o.toString !== undefined    //true

  不过这种方式存在问题,当这个x的值本身就是undefined时,上面o.x !=== undefined这条语句就会返回false。

枚举属性

  所谓可枚举的属性就是使用for/in循环可以遍历到的属性。一般来说我们自己写的属性(包括函数)都是可枚举的,除非用下文提到的一个方法将它转换为不可枚举。注意,可枚举属性不仅可以是自有属性,还可以是继承而来的属性。

  在ECMAScript5的标准中提供了两个用来获取可枚举属性名的函数,第一个是Object.keys()返回对象中所有的自有可枚举属性名组成的组数。第二个是Object.getOwnPropertyNames()返回对象中所有的自有属性名组成的数组。

  先不管definedProperties(下文将会讲),总之上面先创建了一个对象拥有一个x属性值为1可枚举,和一个y属性值为hello不可枚举。然后执行Object.keys()方法和Object.getOwnPropertyNames()方法分别得到了上面这两个数组。

属性setter和getter

  如果你对JavaBean或者说POJO有一定的了解,那么学这两个东西也许还没开始就能猜出十有八九。通常来说对象里面的属性都是键值对(属性名:属性值)的形式存在的,但是js允许某些属性不以这种形式存在,取而代之的是setter和getter。或许这样说很难解释,看下面这个例子你也许会很快了解。

 1 var p = {
 2 //记录笛卡尔坐标系的位置
 3     x:1.0,
 4     y:1.0,
 5
 6 //r是由x和y转为极坐标后的极径
 7 //r的setter和getter
 8     get r() {
 9         return Math.sqrt(this.x*this.x + this.y*this.y);
10     },
11     set r(_new) {
12         var _old = Math.sqrt(this.x*this.x + this.y*this.y);
13         var ratio = _new/_old;
14         this.x *= ratio;
15         this.y *= ratio;
16     },
17
18 //θ为极角
19     get theta() {
20         return Math.atan2(this.y, this.x);
21     }
22 };

  上面这个对象我们在外部看来它拥有四个属性,事实上你用Object.keys()函数也能得到["x", "y", "r", "theta"]这个值。然而对象内部真实地却只有两个变量组成的属性x和y。其中属性r和属性theta都是用setter或(和)getter函数模拟出来的。由于theta只有getter,所以这是一个只读属性。

  注意这个例子中setter和getter的书写格式,get或set替代原本用来声明函数的关键字function,并且get和set与函数体之间没有通常键值对用于分割的冒号,这个函数名就是我们模拟出来的对象的属性。

属性的特性

  对象的属性有四个特性:①值、②可写、③可枚举、④可配置。对于由getter和setter模拟出来的属性也有四个特性:①读(get)、②写(set)、③可枚举、④可配置。

获取属性描述符

在ECMAScript5中可以利用Object.getOwnPropertyDescriptor()来获取某一个特定属性的属性描述符,看下面的例子:

  这里使用Object.getOwnPropertyDescriptor()方法对上面定义的“笛卡尔坐标系与极坐标系互转的对象p”进行了属性描述符的获取(在chrome中)。可以看出默认情况下我们定义的属性是可写、可枚举和可配置的。而模拟出来的属性默认也都是可枚举和可配置的。如果它不存在getter或setter,那么在相应的属性位置显示为undefined。如果这个对象(p)不包含该属性(nothing),那么直接返回undefined。

  注意Object.getOwnPropertyDescriptor()该方法只返回自有属性,也就是说要想获得继承属性的特性需要遍历原型链。

设置属性特性

  上面通过Object.getOwnPropertyDescriptor()这个方法可以获取某个对象的属性描述符,如果我们想设置某个对象的某个属性的特性,可以使用Object.defineProperty()这个方法。看下面的例子:

1 var a = Object.defineProperty({}, "x", {
2     value:1,
3     writable:true,
4     enumerable:true,
5     configurable:true});
6 //上面这段代码等价于 var a = {x:1};

对象的三个属性

  每个对象(null除外)都有与之相关联的三个属性,分别是原型、类和可扩展性。

原型属性

  每一个对象(null除外)都有原型,它在js中是如此的重要,以至于只要你想学好js的对象和函数部分这是你一道跨不过的坎,上文曾稍微谈了一下原型对象,还强烈建议读者阅读这篇博客,如果你对原型对象还不是特别了解,还是建议你去看一下这篇博客,或者其它的讲原型对象的博客,这真的很重要。

  在ECMAScript5中,你可以通过Object.getPrototypeOf()这个方法来获取某个对象的原型对象。也可以使用每个对象的isPrototypeOf()方法来检测继承关系。

1 var a = {x:1};
2 var b = Object.create(a);
3 b.y = 2;
4 Object.getPrototypeOf(b);    //a
5 a.isPrototypeOf(b)    //true

类属性

  类属性是一个字符串,在ECMAScript3和ECMAScript5中并没有提供对这个字符串的修改。对这个字符串的获取就是我们常说的toString()方法。但是某些时候toString()这个函数经常被重写,为了能调用正确版本的toString()方法必须间接调用Function.call()方法。Function()调用方式与Function.call()的调用方式的区别就是函数执行Function这个函数执行时的this指向不同。关于Function.call和Function.apply这两个方法的具体使用将在后面的博客中讲解。如果你有兴趣也可以先参考一下这篇博客

var a=[1,2];    //定义一个数组
a.toString();    //得到字符串"1,2"
Object.prototype.toString.call(a);    //得到字符串"[object Array]"

可扩展性

  js中的对象的可扩展性直接决定了这个对象是否能添加新属性,如果这个对象不具有可扩展性,那么他将和java等静态语言中的对象一样,无法动态添加属性了。在对象属性的扩展性这方面,ECMAScript5提供了三组常用的函数:

1 Object.isExtensible(a);    //判断对象a是否可扩展。
2 Object.preventExtensions(a);    //设置a为不可扩展
3 Object.isSealed(b);    //判断对象b是否被封闭
4 Object.seal(b);    //封闭对象b
5 Object.isFrozen(c);    //判断对象c是否被冻结
6 Object.freeze(c);    //冻结对象c

  注意上面的设置不可扩展、封闭和冻结等动作都是不可逆的,其中由不可扩展到冻结对属性的限制越来越严。不可扩展只是不允许添加新的属性,封闭不仅不可添加新的属性还将所有的属性都设置为了不可配置,也就说已有的属性也不让删除,而冻结就更严格了,在封闭的基础上还将所有属性设置为了只读(模拟属性中的setter除外)。

序列化对象

  对象序列化是指将对象的状态转化为字符串,也可以将字符串还原为对象。ECMAScript5提供了JSON.stringify()和JSON.parse()来序列化和还原JavaScript对象。JSON的全称是JavaScript Object Notation的缩写,意思是”JavaScript对象表示法“。JSON的语法是JavaScript语法的子集,它并不能表示JavaScript中的所有值。由于序列化这个功能不太常用,所以这里就不展开讲解了,有兴趣的同学可以参考这篇博客

对象方法

  上文已经谈到过js中的所有对象都从Object.prototype中继承了属性,且这些属性主要是方法。我们前面已经涉及过hasOwnProperty()、propertyIsEnumerable()和isPrototypeOf()这三个方法。静态方法方面涉及了Object.create()和Object.getPrototypeOf()等方法。

toString()方法

  一般将对象转换为字符串时js会自动调用这个方法。比如:"now is " + new Date();这句话中js会自动调用日期这个对象的toString()方法将其转换为字符串再与前面的字符串相连接。该方法经常被重写,在后面的博客中将介绍怎么重写这个方法。

valueOf()方法

  这个方法和上面的toString()方法差不多,一般也是被js自动调用在期待数字的时候,比如:23==new String("23");这个比较中js会自动调用new String("23")这个对象的valueOf方法,将其转换为字符串再进行判等比较。

原文地址:https://www.cnblogs.com/flyor/p/8511107.html

时间: 2024-11-07 22:59:06

JavaScript基础入门教程(四)的相关文章

JavaScript基础入门教程(二)

说明 前一篇博客介绍了js以及一些关于js基本类型的简单知识,本篇博客将详细介绍js的基础类型,捎带介绍对象类型,更详细的对象类型的说明将后续再讲. js中类型的说明 js中的类型分为基本类型和对象类型,其中基本类型包括:①数字.②字符串.③布尔值.此外还有两个原始值null和undefined.其中对象包括数组对象.函数对象和普通对象,普通对象是"命名值"的无序集合,而数组对象是带编号的值的有序集合.JavaScript核心还定义了三种有用的类:日期(Data)类.正则(RegExp

JavaScript基础入门教程(三)

说明 前面的两篇博客介绍了js中的基本知识中的变量类型.标识符等.这篇博客主要谈表达式以及运算符. 原始表达式 原始表达式就是表达式中最小的,不能在分割的表达式,一般指变量.常数直接量.关键字(true.false.null.this),这里需要注意的是undefined是一个全局变量,而不是关键字. 对象和数组的初始化表达式 数组初始化表达式是一个方括号,中间放置数组中的元素,元素间用逗号分割.元素可以是表达式.数组.空. 1 [] //一个空数组,数组里面没有元素 2 [1+2,3+4] /

JavaScript基础入门教程(一)

js的数据类型 虽然js属于弱类型语言,但其总共有6种数据类型.弱类型的意思是定义的变量没有类型的区分,比如先执行 var num=123; 定义了一变量num存储整数,可接下来执行 num="hello world"; 将字符串赋值给num变量并不会报错,是合法的. js的弱类型相对于强类型的语言来说看起来好像很好用,其实也不尽然,下面列举了一些字符串和整数进行操作时候的一些因为js的弱类型带来的问题. 32 + 32 // 结果是 64 "32" + 32 //

Android基础入门教程——8.3.7 Paint API之—— Xfermode与PorterDuff详解(四)

Android基础入门教程--8.3.7 Paint API之-- Xfermode与PorterDuff详解(四) 标签(空格分隔): Android基础入门教程 本节引言: 上节我们写了关于Xfermode与PorterDuff使用的第一个例子:圆角&圆形图片ImageView的实现, 我们体会到了PorterDuff.Mode.DST_IN给我们带来的好处,本节我们继续来写例子练练手, 还记得Android基础入门教程--8.3.2 绘图类实战示例给大家带来的拔掉美女衣服的实现吗? 当时我

Android基础入门教程——7.5.1 WebView(网页视图)基本用法

Android基础入门教程--7.5.1 WebView(网页视图)基本用法 标签(空格分隔): Android基础入门教程 本节引言 本节给大家带来的是Android中的一个用于显示网页的控件:WebView(网页视图),现在Android应用 层开发的方向有两种:客户端开发和HTML5移动端开发!所谓的HTML5端就是:HTML5 + CSS + JS来构建 一个网页版的应用,而这中间的媒介就是这个WebView,而Web和网页端可以通过JS来进行交互,比如, 网页读取手机联系人,调用手机相

Android基础入门教程——8.1.3 Android中的13种Drawable小结 Part 3

Android基础入门教程--8.1.3 Android中的13种Drawable小结 Part 3 标签(空格分隔): Android基础入门教程 本节引言: 本节我们来把剩下的四种Drawable也学完,他们分别是: LayerDrawable,TransitionDrawable,LevelListDrawable和StateListDrawable, 依旧贴下13种Drawable的导图: 1.LayerDrawable 层图形对象,包含一个Drawable数组,然后按照数组对应的顺序来

Android基础入门教程——8.1.2 Android中的13种Drawable小结 Part 2

Android基础入门教程--8.1.2 Android中的13种Drawable小结 Part 2 标签(空格分隔): Android基础入门教程 本节引言: 本节我们继续来学习Android中的Drawable资源,上一节我们学习了: ColorDrawable:NinePatchDrawable: ShapeDrawable:GradientDrawable!这四个Drawable~ 而本节我们继续来学习接下来的五个Drawable,他们分别是: BitmapDrawable:Insert

2015年最新Android基础入门教程目录(完结版)

2015年最新Android基础入门教程目录(完结版) 标签(空格分隔): Android基础入门教程 前言: 关于<2015年最新Android基础入门教程目录>终于在今天落下了帷幕,全套教程 共148节已编写完毕,附上目录,关于教程的由来,笔者的情况和自学心得,资源分享 以及一些疑问等可戳:<2015最新Android基础入门教程>完结散花~ 下面是本系列教程的完整目录: 第一章:环境搭建与开发相关(已完结 10/10) Android基础入门教程--1.1 背景相关与系统架构

Android基础入门教程——10.10 传感器专题(1)——相关介绍

Android基础入门教程--10.10 传感器专题(1)--相关介绍 标签(空格分隔): Android基础入门教程 1.传感器相关介绍: 说到传感器,相信大家都不会陌生吧,比如微信的摇一摇就用到了加速度传感器: 传感器的定义:一种物理设备或者生物器官,能够探测.感受外界的信号,物理条件(如光,热, 适度)或化学组成(如烟雾),并将探知的信息传递给其他的设备或者器官! 传感器的种类:可以从不同的角度对传感器进行划分,转换原理(传感器工作的基本物理或化学 效应):用途:输出信号以及制作材料和工艺