JavaScript学习——对象
2015-08-04 本文已影响130人
ting723
1. 本文是在学习廖雪峰先生的JavaScrip教程 后的归纳
一、标准对象
-
typeof
- JavaScript的世界,一切都是对象
- 用
typeof
操作符获取对象的类型,总是返回一个字符串
typeof 123; // 'number' typeof NaN; // 'number' typeof 'str'; // 'string' typeof true; // 'boolean' typeof undefined; // 'undefined' typeof Math.abs; // 'function' typeof null; // 'object' typeof []; // 'object' typeof {}; // 'object'
-
number
和string
、boolean
、function
和undefined
有别于其他类型 -
null
、Array
、{}
的类型是object
,故typeof
无法区分三者
- 包装对象
-
number
、boolean
、和string
都有包装类型,使用new
创建
var n = new Number(123); // 123,生成了新的包装类型 var b = new Boolean(true); // true,生成了新的包装类型 var s = new String('str'); // 'str',生成了新的包装类型
- 包装对象虽和原来的值一样,但类型已经发生变化,都变成
object
- 尽量不要使用包装类型,尤其是
string
类型 -
Nubmer
、Boolean
、String
没写new
时,可以被当成普通函数,把任何类型的数据转换为number
、boolean
、string
类型(不是包装类型)
var n = Number('123'); // 123,相当于parseInt()或parseFloat() typeof n; // 'number' var b = Boolean('true'); // true typeof b; // 'boolean' var b2 = Boolean('false'); // true! 'false'字符串转换结果为true!因为它是非空字符串! var b3 = Boolean(''); // false var s = String(123.45); // '123.45' typeof s; // 'string'
-
- 总结
- 不要使用
new Number()、new Boolean()、new String()
创建包装对象; - 用
parseInt()
或parseFloat()
来转换任意类型到number
; - 用
String()
来转换任意类型到string
,或者直接调用某个对象的toString()
方法; - 通常不必把任意类型转换为
boolean
再判断,因为可以直接写if (myVar) {...}
; -
typeof
操作符可以判断出number、boolean、string、function和undefined
; - 判断
Array
要使用Array.isArray(arr)
; - 判断
null
请使用myVar === null
; - 判断某个全局变量是否存在用
typeof window.myVar === 'undefined'
; - 函数内部判断某个变量是否存在用
typeof myVar === 'undefined'
-
null
和undefined
没有toString()
方法 - 特殊情况:
number
使用toString()
方法
123..toString();//两个点 (123).toString();
- 不要使用
二、Date
- Date表示日期和时间
- 获取系统时间如下:
var now = new Date(); now; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST) now.getFullYear(); // 2015, 年份 now.getMonth(); // 5, 月份,注意月份范围是0~11,5表示六月 now.getDate(); // 24, 表示24号 now.getDay(); // 3, 表示星期三 now.getHours(); // 19, 24小时制 now.getMinutes(); // 49, 分钟 now.getSeconds(); // 22, 秒 now.getMilliseconds(); // 875, 毫秒数 now.getTime(); // 1435146562875, 以number形式表示的时间戳
- 创建一个指定日期和时间的
Date
对象,可以用:
var d = new Date(2015, 5, 19, 20, 15, 30, 123); d; // Fri Jun 19 2015 20:15:30 GMT+0800 (CST)
- JavaScript的月份范围用整数表示是
0-11
- 创建一个指定日期和时间的方法是解析一个符合
ISO 8601
格式的字符串 -
Data.parse('2015-06-24T19:49:22.875+08:00');
返回一个时间戳;该时间戳很容易转换成Date
- 时区
-
Date
对象表示的时间总是按浏览器所在时区显示的,不过,我们既可以显示本地时间,也可以显示调整后的UTC时间
var d = new Date(1435146562875); d.toLocaleString(); // '2015/6/24 下午7:49:22',本地时间(北京时区+8:00),显示的字符串与操作系统设定的格式有关 d.toUTCString(); // 'Wed, 24 Jun 2015 11:49:22 GMT',UTC时间,与本地时间相差8小时
- 时间戳: 是一个自增的整数,它表示从1970年1月1日零时整的GMT时区开始的那一刻,到现在的毫秒数。
-
三、RegExp
- 基本
-
\d
可以匹配一个数字,\w
可以匹配一个字母或数字 -
.
可以匹配任意字符,*
表示任意个字符,用+
表示至少一个字符,?
表示0个或1个字符,用{n}
表示n个字符,用`{n,m}表示n-m个字符 -
\s
可以匹配一个字符(也包括Tab等字符)
-
- 进阶
-
[]
表示范围,A|B
可以匹配A或B -
^
表示行的开头,^\d
表示必须以数字开头 -
&
表示行的结束
-
- JavaScript中使用正则表达式
- 写法一:
/正则表达式/
- 写法二:
new RegExp('正则表达式
)`创建一个RegExp对象 - 示例:
var re = /^\d{3}\=\d{3,8}$/; re.test('010-12345');//true
- RegExp对象
test()
方法用于测试给定的字符串是否符合规定 - 切分字符串:用正则表达式来把不规范的输入转化成正确的数组
- 分组:提取子串的功能,通过
()
表示的就是要提取的分组(Group)
在正则表示式中定义了组,就可以在var re = /^(\d{3})-(\d{3,8})$/; re.exec('010-12345'); // ['010-12345', '010', '12345'] re.exec('010 12345'); // null
RegExp
对象上用exec()
方法提取出子串来
exec()
方法在匹配成功后,会返回一个Array
,第一元素始终是原始字符串本身,后面的字符串表示匹配成功的子串
exec()
方法在匹配失败后,返回null
- 正则表达式默认的是贪婪匹配,也就是匹配尽可能多的字符
-
\d+
加个?
就可以采用贪婪匹配
- 写法一:
- 全局搜索
-
g
表示全局匹配 -
var r1 = /test/g;
等价于var r2=new RegExp('test','g')
; - 全局匹配可以多次执行
exec()
方法来搜索一个匹配的字符串,当指定g
标志后,每次运行exec()
,正则表达式本身会更新lastIndex
属性,表示上次匹配到的最后索引
var s = 'JavaScript, VBScript, JScript and ECMAScript'; var re=/[a-zA-Z]+Script/g; // 使用全局匹配: re.exec(s); // ['JavaScript'] re.lastIndex; // 10 re.exec(s); // ['VBScript'] re.lastIndex; // 20 re.exec(s); // ['JScript'] re.lastIndex; // 29 re.exec(s); // ['ECMAScript'] re.lastIndex; // 44 re.exec(s); // null,直到结束仍没有匹配到
- 全局匹配类似搜索,因此,不能使用
/^...$/
,那样只会最多匹配一次 - 正则表达式还可以指定
i
标志,表示忽略大小写,m
标志,表示执行多行匹配
-
四、JSON
- 定义
- JSON是JavaScript Object Notation的缩写,是一种数据交换格式
- 由道格拉斯·克罗克福特(Douglas Crockford)发明
- JSON实际上是JavaScript的一个子集
- JSON数据类型和JavaScript基本一样:
number
,boolean
,string
,null
,array
,object
以及前面的任意组合 - JSON定死了字符集为
UTF-8
,对多语言没有问题 - 为了统一解析,JSON的字符串规定必须用
""
,Object
的键也必须用双引号""
- 把任何JavaScript对象变成JSON,就是把这个对象序列化成一个JSON格式的字符串
- 收到一个JSON格式的字符串,只需要把它反序列化成一个JavaScript对象
- 序列化
- 示例:
var xiaoming = { name: '小明', age: 14, gender: true, height: 1.65, grade: null, 'middle-school': '\"W3C\" Middle School', skills: ['JavaScript', 'Java', 'Python', 'Lisp'] }; JSON.stringify(xiaoming); // '{"name":"小明","age":14,"gender":true,"height":1.65,"grade":null,"middle-school":"\"W3C\" Middle School","skills":["JavaScript","Java","Python","Lisp"]}'
- 输出比较规范:
JSON.stringify(xiaoming, null, ' ');
-
JSON.stringify(xiaoming,['name','skill'],' ');
第二个参数用来筛选对象的键值,只输出指定的属性,则Array
-
JSON.stringify(xiaoming, convert, ' ');
还可以传入一个函数(function convert(key,value){}
) - 还可以为对象定义一
toJSON()
方法
var xiaoming = { name: '小明', age: 14, gender: true, height: 1.65, grade: null, 'middle-school': '\"W3C\" Middle School', skills: ['JavaScript', 'Java', 'Python', 'Lisp'], toJSON: function () { return { // 只输出name和age,并且改变了key: 'Name': this.name, 'Age': this.age }; } }; JSON.stringify(xiaoming); // '{"Name":"小明","Age":14}'
- 反序列化
- 拿到一JSON格式的字符串,我们直接用
JSON.parse()
把它变成一个JavaScript对象 - 示例:
JSON.parse('[1,2,3,true]'); // [1, 2, 3, true] JSON.parse('{"name":"小明","age":14}'); // Object {name: '小明', age: 14} JSON.parse('true'); // true JSON.parse('123.45'); // 123.45
-
JSON.parse()
可以接收一个函数,用来转换解析出来的属性 - 示例:
JSON.parse('{"name":"小明","age":14}', function (key, value) { // 把number * 2: if (key === 'name') { return value + '同学'; } return value; });// Object {name: '小明同学', age: 14}
- 拿到一JSON格式的字符串,我们直接用
五、面向对象编程
- 面向对象
- 类和实例是大多数面向对象编程的基本概念
- JavaScript中,JavaScript不区分类和实例的概念,而是通过原型(
prototype
)来实现面向对象编程 - 示例:
var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is running...'); } }; var xiaoming = { name: '小明' }; xiaoming.__proto__ = Student;
- JavaScript的原型链和Java的Class的区别就是在于,JavaScript没有
"Class"
概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已 - 编写JavaScript代码时,不要直接用
obj.__proto__
去改变一个对象的原型,可以使用Object.create()
方法可以传入一个原型对象,并创建一个基于该原型的新对象,但新对象什么属性都没有 - 示例:
function createStudent(name) { // 基于Student原型创建一个新对象: var s = Object.create(Student); // 初始化新对象: s.name = name; return s; }
六、创建对象
- 原型链
- JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象
- 当使用
obj.xxx
访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没找到,就到Object.prototype
对象,最后还没找到,就只能返回undefined
示例:
var arr=[1,2,3]; 原型链是: arr---->Array.prototype-->Object.prototype--->null 其中`Array.prototype`定义了`indexOf()`、`shift()`等方法, 因此可以在所有`Array`对象上直接调用执行方法 function foo(){ return 0; } foo--->Function.prototype--->Object.prototype--->null 其中Function.prototype定义了apply()方法
- 如果原型链很长,那么访问一个对象的属性会因为耗费更多时间查找而变得更慢,因此,原型链不宜太长
- 构造函数
- 除了
{...}
直接创建对象外,还可以通过构造函数的方法来创建对象 - 示例:
function Student(name) { this.name = name; this.hello = function () { alert('Hello, ' + this.name + '!'); } } var xiaoming=new Student('xiaoming'); 原型链如下: xiaoming---->Student.prototype--->Object.prototype--->null
- 在JavaScript中,可以用关键字
new
来调用这个函数,并返回一个对象 - 不写new,是个普通函数,加上
new
就变成一个构造函数,它绑定this
指向新创建的对象,并默认返回this
,即不需要在最后加return this
-
new Student()
创建的对象还从原型上获得了一constructor
属性,其指向Student
本身
xiaoming.constructor === Student.prototype.constructor; // true Student.prototype.constructor === Student; // true Object.getPrototypeOf(xiaoming) === Student.prototype; // true xiaoming instanceof Student; // true
- 让创建对象共享一个函数时,根据对象的属性查找原则,可以把函数移到对象共同的原型上
- 为了区分普通函数和构造函数,按照约定,构造函数首字母大写,而普通函数首字母小写
- 一个常用的编程模式:
function Student(props) { this.name = props.name || '匿名'; // 默认值为'匿名' this.grade = props.grade || 1; // 默认值为1 } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); }; function createStudent(props) { return new Student(props || {}) } var xiaoming = createStudent({ name: '小明' }); xiaoming.grade; // 1 优点: 这个`createStudent()`函数有几个优点: a. 不需要new来调用 b. 参数非常灵活,可以传参,也可以不传参
- 除了
七、原型继承
- 继承
- Java等传统的基于Class语言,本质上是扩展一个已有的
Class
,并生成新的Subclass,由于这类语言严格区分类和实例,继承实际上是类型的扩展 - JavaScript 采用的原型继承,无法直接扩展一个Class
- JavaScript的原型继承实现方式就是:
- 定义新的构造函数,并在内部用
call()
调用希望"继承"的构造函数,并绑定this
- 借助中间函数
F
实现原型链继承,最好通过封装的extendes
函数完成 - 继续在新的构造函数的原型上定义新方法
- 定义新的构造函数,并在内部用
- Java等传统的基于Class语言,本质上是扩展一个已有的