一、标准库

1. Object 对象

  • Object对象的原生方法分成两类
    • Object本身的方法
    • Object的实例方法
  1. // Object对象本身的方法
  2. Object.print = function (o) { console.log(o) };
  3. // Object的实例方法
  4. Object.prototype.print = function () {
  5. console.log(this);
  6. };
  7. var obj = new Object();
  8. obj.print() // Object

Object()

  • Object本身是一个函数,可以当作工具方法使用,将任意值转为对象
  • 这个方法常用于保证某个值一定是对象
  1. var obj = Object();
  2. // 等同于
  3. var obj = Object(undefined);
  4. var obj = Object(null);
  5. obj instanceof Object // true
  6. // instanceof运算符用来验证,一个对象是否为指定的构造函数的实例
  7. // 参数为各种原始类型的值
  8. var obj = Object(1);
  9. obj instanceof Object // true
  10. obj instanceof Number // true
  11. var obj = Object('foo');
  12. obj instanceof Object // true
  13. obj instanceof String // true
  14. var obj = Object(true);
  15. obj instanceof Object // true
  16. obj instanceof Boolean // true
  17. // 参数为一个对象
  18. var arr = [];
  19. var obj = Object(arr); // 返回原数组
  20. obj === arr // true
  21. var value = {};
  22. var obj = Object(value) // 返回原对象
  23. obj === value // true
  24. var fn = function () {};
  25. var obj = Object(fn); // 返回原函数
  26. obj === fn // true
  27. // 写一个判断变量是否为对象的函数
  28. function isObject(value) {
  29. return value === Object(value);
  30. }
  31. isObject([]) // true
  32. isObject(true) // false

Object 构造函数

  • Object不仅可以当作工具函数使用,还可以当作构造函数使用
  • 即前面可以使用new命令
  1. var obj = new Object();
  2. // var obj = new Object()的写法与 var obj = {}是等价的
  3. var o1 = {a: 1};
  4. var o2 = new Object(o1);
  5. o1 === o2 // true
  6. var obj = new Object(123);
  7. obj instanceof Number // true
  8. // Object(value)与new Object(value)两者的语义是不同的
  9. // Object(value)表示将value转成一个对象
  10. // new Object(value)则表示新生成一个对象,它的值是value

Object.keys(),Object.getOwnPropertyNames()

  1. var obj = {
  2. p1: 123,
  3. p2: 456
  4. };
  5. Object.keys(obj) // ["p1", "p2"]
  6. var obj = {
  7. p1: 123,
  8. p2: 456
  9. };
  10. Object.getOwnPropertyNames(obj) // ["p1", "p2"]
  11. // 对于一般的对象来说,Object.keys()和Object.getOwnPropertyNames()返回的结果是一样的
  12. // 只有涉及不可枚举属性时,才会有不一样的结果
  13. var a = ['Hello', 'World'];
  14. Object.keys(a) // ["0", "1"]
  15. Object.getOwnPropertyNames(a) // ["0", "1", "length"]
  16. // 数组的length属性是不可枚举的属性
  17. // 由于 JavaScript 没有提供计算对象属性个数的方法,所以可以用这两个方法代替
  18. var obj = {
  19. p1: 123,
  20. p2: 456
  21. };
  22. Object.keys(obj).length // 2
  23. Object.getOwnPropertyNames(obj).length // 2
  24. // 一般情况下,几乎总是使用Object.keys方法,遍历对象的属性

Object 本身的其他方法

  • 对象属性模型的相关方法
    • Object.getOwnPropertyDescriptor():获取某个属性的描述对象
    • Object.defineProperty():通过描述对象,定义某个属性
    • Object.defineProperties():通过描述对象,定义多个属性
  • 控制对象状态的方法
    • Object.preventExtensions():防止对象扩展
    • Object.isExtensible():判断对象是否可扩展
    • Object.seal():禁止对象配置
    • Object.isSealed():判断一个对象是否可配置
    • Object.freeze():冻结一个对象
    • Object.isFrozen():判断一个对象是否被冻结。
  • 原型链相关方法
  • Object.create():该方法可以指定原型对象和属性,返回一个新的对象
  • Object.getPrototypeOf():获取对象的Prototype对象

Object 的实例方法

  • 主要有以下六个
  • Object.prototype.valueOf():返回当前对象对应的值
  • Object.prototype.toString():返回当前对象对应的字符串形式
  • Object.prototype.toLocaleString():返回当前对象对应的本地字符串形式
  • Object.prototype.hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性
  • Object.prototype.isPrototypeOf():判断当前对象是否为另一个对象的原型
  • Object.prototype.propertyIsEnumerable():判断某个属性是否可枚举。
  1. // valueOf方法的作用是返回一个对象的“值”,默认情况下返回对象本身
  2. var obj = new Object();
  3. obj.valueOf() === obj // true
  4. var obj = new Object();
  5. 1 + obj // "1[object Object]"
  6. var obj = new Object();
  7. obj.valueOf = function () {
  8. return 2;
  9. };
  10. 1 + obj // 3
  11. // toString方法的作用是返回一个对象的字符串形式,默认情况下返回类型字符串
  12. var o1 = new Object();
  13. o1.toString() // "[object Object]"
  14. var o2 = {a:1};
  15. o2.toString() // "[object Object]"
  16. var obj = new Object();
  17. obj.toString = function () {
  18. return 'hello';
  19. };
  20. obj + ' ' + 'world' // "hello world"
  21. // 数组、字符串、函数、Date 对象都分别部署了自定义的toString方法
  22. // 覆盖了Object.prototype.toString方法
  23. [1, 2, 3].toString() // "1,2,3"
  24. '123'.toString() // "123"
  25. (function () {
  26. return 123;
  27. }).toString()
  28. // "function () {
  29. // return 123;
  30. // }"
  31. (new Date()).toString()
  32. // "Tue May 10 2016 09:11:31 GMT+0800 (CST)"
  33. // 上面代码中,数组、字符串、函数、Date 对象调用toString方法
  34. // 并不会返回[object Object],因为它们都自定义了toString方法,覆盖原始方法
  35. // toString() 的应用:判断数据类型
  36. var obj = {};
  37. obj.toString() // "[object Object]"
  38. // 其中第二个Object表示该值的构造函数。这是一个十分有用的判断数据类型的方法
  39. // 由于实例对象可能会自定义toString方法,覆盖掉Object.prototype.toString方法
  40. // 所以为了得到类型字符串,最好直接使用Object.prototype.toString方法
  41. // Object.prototype.toString.call(value)
  42. // 不同数据类型的Object.prototype.toString方法返回值如下
  43. 数值:返回[object Number]
  44. 字符串:返回[object String]
  45. 布尔值:返回[object Boolean]
  46. undefined:返回[object Undefined]
  47. null:返回[object Null]
  48. 数组:返回[object Array]
  49. arguments 对象:返回[object Arguments]
  50. 函数:返回[object Function]
  51. Error 对象:返回[object Error]
  52. Date 对象:返回[object Date]
  53. RegExp 对象:返回[object RegExp]
  54. 其他对象:返回[object Object]
  55. Object.prototype.toString.call(2) // "[object Number]"
  56. Object.prototype.toString.call('') // "[object String]"
  57. Object.prototype.toString.call(true) // "[object Boolean]"
  58. Object.prototype.toString.call(undefined) // "[object Undefined]"
  59. Object.prototype.toString.call(null) // "[object Null]"
  60. Object.prototype.toString.call(Math) // "[object Math]"
  61. Object.prototype.toString.call({}) // "[object Object]"
  62. Object.prototype.toString.call([]) // "[object Array]"
  63. // 利用这个特性,可以写出一个比typeof运算符更准确的类型判断函数
  64. var type = function (o){
  65. var s = Object.prototype.toString.call(o);
  66. return s.match(/\[object (.*?)\]/)[1].toLowerCase();
  67. };
  68. type({}); // "object"
  69. type([]); // "array"
  70. type(5); // "number"
  71. type(null); // "null"
  72. type(); // "undefined"
  73. type(/abcd/); // "regex"
  74. type(new Date()); // "date"
  75. // 在上面这个type函数的基础上,还可以加上专门判断某种类型数据的方法
  76. var type = function (o){
  77. var s = Object.prototype.toString.call(o);
  78. return s.match(/\[object (.*?)\]/)[1].toLowerCase();
  79. };
  80. ['Null',
  81. 'Undefined',
  82. 'Object',
  83. 'Array',
  84. 'String',
  85. 'Number',
  86. 'Boolean',
  87. 'Function',
  88. 'RegExp'
  89. ].forEach(function (t) {
  90. type['is' + t] = function (o) {
  91. return type(o) === t.toLowerCase();
  92. };
  93. });
  94. type.isObject({}) // true
  95. type.isNumber(NaN) // true
  96. type.isRegExp(/abc/) // true
  97. // Object.prototype.toLocaleString方法与toString的返回结果相同,也是返回一个值的字符串形式
  98. var obj = {};
  99. obj.toString(obj) // "[object Object]"
  100. obj.toLocaleString(obj) // "[object Object]"
  101. // 这个方法的主要作用是留出一个接口,让各种不同的对象实现自己版本的toLocaleString
  102. // 用来返回针对某些地域的特定的值
  103. var person = {
  104. toString: function () {
  105. return 'Henry Norman Bethune';
  106. },
  107. toLocaleString: function () {
  108. return '白求恩';
  109. }
  110. };
  111. person.toString() // Henry Norman Bethune
  112. person.toLocaleString() // 白求恩
  113. // 目前,主要有三个对象自定义了toLocaleString方法
  114. Array.prototype.toLocaleString()
  115. Number.prototype.toLocaleString()
  116. Date.prototype.toLocaleString()
  117. // 举例来说,日期的实例对象的toString和toLocaleString返回值就不一样
  118. // 而且toLocaleString的返回值跟用户设定的所在地域相关
  119. var date = new Date();
  120. date.toString() // "Tue Jan 01 2018 12:01:33 GMT+0800 (CST)"
  121. date.toLocaleString() // "1/01/2018, 12:01:33 PM"
  122. // Object.prototype.hasOwnProperty方法接受一个字符串作为参数,返回一个布尔值
  123. // 表示该实例对象自身是否具有该属性
  124. var obj = {
  125. p: 123
  126. };
  127. obj.hasOwnProperty('p') // true
  128. obj.hasOwnProperty('toString') // false
  129. // 上面代码中,对象obj自身具有p属性,所以返回true
  130. toString属性是继承的,所以返回false

2. 属性描述对象

  • JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为
  • 比如该属性是否可写、可遍历等等
  • 这个内部数据结构称为“属性描述对象”(attributes object)
  • 每个属性都有自己对应的属性描述对象,保存该属性的一些元信息


  1. // 属性描述对象的一个例子
  2. {
  3. value: 123, // 该属性的属性值,默认为undefined
  4. writable: false, // 表示属性值(value)是否可改变(即是否可写),默认为true
  5. enumerable: true, // 表示该属性是否可遍历
  6. configurable: false, // 表示可配置性,默认为true
  7. get: undefined, // get是一个函数,表示该属性的取值函数(getter),默认为undefined
  8. set: undefined // set是一个函数,表示该属性的存值函数(setter),默认为undefined
  9. }

Object.getOwnPropertyDescriptor(),Object.getOwnPropertyNames

  1. // Object.getOwnPropertyDescriptor()方法可以获取属性描述对象
  2. // 它的第一个参数是目标对象,第二个参数是一个字符串,对应目标对象的某个属性名
  3. var obj = { p: 'a' };
  4. Object.getOwnPropertyDescriptor(obj, 'p')
  5. // Object { value: "a",
  6. // writable: true,
  7. // enumerable: true,
  8. // configurable: true
  9. // }
  10. // 注意,Object.getOwnPropertyDescriptor()方法只能用于对象自身的属性,不能用于继承的属性
  11. var obj = { p: 'a' };
  12. Object.getOwnPropertyDescriptor(obj, 'toString')
  13. // undefined
  14. // Object.getOwnPropertyNames方法返回一个数组,成员是参数对象自身的全部属性的属性名
  15. // 不管该属性是否可遍历
  16. var obj = Object.defineProperties({}, {
  17. p1: { value: 1, enumerable: true },
  18. p2: { value: 2, enumerable: false }
  19. });
  20. Object.getOwnPropertyNames(obj)
  21. // ["p1", "p2"]
  22. Object.keys([]) // []
  23. Object.getOwnPropertyNames([]) // [ 'length' ]
  24. // 这跟Object.keys的行为不同,Object.keys只返回对象自身的可遍历属性的全部属性名
  25. Object.keys(Object.prototype) // []
  26. Object.getOwnPropertyNames(Object.prototype)
  27. // ['hasOwnProperty',
  28. // 'valueOf',
  29. // 'constructor',
  30. // 'toLocaleString',
  31. // 'isPrototypeOf',
  32. // 'propertyIsEnumerable',
  33. // 'toString']

Object.defineProperty(),Object.defineProperties(),Object.prototype.propertyIsEnumerable()

  1. // Object.defineProperty()方法允许通过属性描述对象,定义或修改一个属性
  2. // 然后返回修改后的对象,它的用法如下
  3. Object.defineProperty(object, propertyName, attributesObject)
  4. var obj = Object.defineProperty({}, 'p', {
  5. value: 123,
  6. writable: false,
  7. enumerable: true,
  8. configurable: false
  9. });
  10. obj.p // 123
  11. obj.p = 246;
  12. obj.p // 123
  13. // 一次性定义或修改多个属性
  14. var obj = Object.defineProperties({}, {
  15. p1: { value: 123, enumerable: true },
  16. p2: { value: 'abc', enumerable: true },
  17. p3: { get: function () { return this.p1 + this.p2 },
  18. enumerable:true,
  19. configurable:true
  20. }
  21. });
  22. obj.p1 // 123
  23. obj.p2 // "abc"
  24. obj.p3 // "123abc"
  25. // 其中,p3属性定义了取值函数get,即每次读取该属性,都会调用这个取值函数
  26. // 注意,一旦定义了取值函数get(或存值函数set)
  27. // 就不能将writable属性设为true,或者同时定义value属性,否则会报错
  28. var obj = {};
  29. Object.defineProperty(obj, 'p', {
  30. value: 123,
  31. get: function() { return 456; }
  32. });
  33. // TypeError: Invalid property.
  34. // A property cannot both have accessors and be writable or have a value
  35. Object.defineProperty(obj, 'p', {
  36. writable: true,
  37. get: function() { return 456; }
  38. });
  39. // TypeError: Invalid property descriptor.
  40. // Cannot both specify accessors and a value or writable attribute
  41. // 上面代码中,同时定义了get属性和value属性,以及将writable属性设为true,就会报错。
  42. // Object.defineProperty()和Object.defineProperties()参数里面的属性描述对象中,
  43. // writable、configurable、enumerable这三个属性的默认值都为false
  44. var obj = {};
  45. Object.defineProperty(obj, 'foo', {});
  46. Object.getOwnPropertyDescriptor(obj, 'foo')
  47. // {
  48. // value: undefined,
  49. // writable: false,
  50. // enumerable: false,
  51. // configurable: false
  52. // }
  53. // 实例对象的propertyIsEnumerable()方法返回一个布尔值,用来判断某个属性是否可遍历
  54. // 注意,这个方法只能用于判断对象自身的属性,对于继承的属性一律返回false
  55. var obj = {};
  56. obj.p = 123;
  57. obj.propertyIsEnumerable('p') // true
  58. obj.propertyIsEnumerable('toString') // false
  59. // 上面代码中,obj.p是可遍历的,而obj.toString是继承的属性

元属性

  • 属性描述对象的各个属性称为“元属性”,因为它们可以看作是控制属性的属性
  1. // value属性是目标属性的值
  2. var obj = {};
  3. obj.p = 123;
  4. Object.getOwnPropertyDescriptor(obj, 'p').value
  5. // 123
  6. Object.defineProperty(obj, 'p', { value: 246 });
  7. obj.p // 246
  8. // 上面代码是通过value属性,读取或改写obj.p的例子
  9. // writable属性是一个布尔值,决定了目标属性的值(value)是否可以被改变
  10. var obj = {};
  11. Object.defineProperty(obj, 'a', {
  12. value: 37,
  13. writable: false
  14. });
  15. obj.a // 37
  16. obj.a = 25;
  17. obj.a // 37
  18. // obj.a的writable属性是false。然后,改变obj.a的值,不会有任何效果
  19. // enumerable(可遍历性)返回一个布尔值,表示目标属性是否可遍历
  20. var obj = {};
  21. 'toString' in obj // true
  22. // 上面代码中,toString不是obj对象自身的属性,但是in运算符也返回true
  23. // 这导致了toString属性也会被for...in循环遍历
  24. // 这显然不太合理,后来就引入了“可遍历性”这个概念
  25. // 只有可遍历的属性,才会被for...in循环遍历
  26. // 同时还规定toString这一类实例对象继承的原生属性,都是不可遍历的
  27. // 这样就保证了for...in循环的可用性
  28. // 具体来说,如果一个属性的enumerable为false,下面三个操作不会取到该属性
  29. for..in循环
  30. Object.keys方法
  31. JSON.stringify方法
  32. var obj = {};
  33. Object.defineProperty(obj, 'x', {
  34. value: 123,
  35. enumerable: false
  36. });
  37. obj.x // 123
  38. for (var key in obj) {
  39. console.log(key);
  40. }
  41. // undefined
  42. Object.keys(obj) // []
  43. JSON.stringify(obj) // "{}"
  44. // 注意,for...in循环包括继承的属性,Object.keys方法不包括继承的属性
  45. // 如果需要获取对象自身的所有属性,不管是否可遍历,可以使用Object.getOwnPropertyNames方法
  46. // configurable(可配置性)返回一个布尔值,决定了是否可以修改属性描述对象
  47. // 也就是说,configurable为false时,value、writable、enumerable和configurable都不能被修改了
  48. var obj = Object.defineProperty({}, 'p', {
  49. value: 1,
  50. writable: false,
  51. enumerable: false,
  52. configurable: false
  53. });
  54. Object.defineProperty(obj, 'p', {value: 2})
  55. // TypeError: Cannot redefine property: p
  56. Object.defineProperty(obj, 'p', {writable: true})
  57. // TypeError: Cannot redefine property: p
  58. Object.defineProperty(obj, 'p', {enumerable: true})
  59. // TypeError: Cannot redefine property: p
  60. Object.defineProperty(obj, 'p', {configurable: true})
  61. // TypeError: Cannot redefine property: p
  62. // 注意,writable只有在false改为true会报错,true改为false是允许的
  63. // 至于value,只要writable和configurable有一个为true,就允许改动

存取器

  • 除了直接定义以外,属性还可以用存取器(accessor)定义
    • 存值函数称为setter,使用属性描述对象的set属性
    • 取值函数称为getter,使用属性描述对象的get属性
  1. var obj = Object.defineProperty({}, 'p', {
  2. get: function () {
  3. return 'getter';
  4. },
  5. set: function (value) {
  6. console.log('setter: ' + value);
  7. }
  8. });
  9. obj.p // "getter"
  10. obj.p = 123 // "setter: 123"
  11. // obj.p定义了get和set属性。obj.p取值时,就会调用get;赋值时,就会调用set
  12. // 写法二
  13. var obj = {
  14. get p() {
  15. return 'getter';
  16. },
  17. set p(value) {
  18. console.log('setter: ' + value);
  19. }
  20. };
  21. // 上面两种写法,虽然属性p的读取和赋值行为是一样的,但是有一些细微的区别
  22. // 一种写法,属性p的configurable和enumerable都为false
  23. // 第二种写法,属性p的configurable和enumerable都为true,因此属性 p 是可遍历的
  24. // 因此属性p是可遍历的。实际开发中,写法二更常用
  25. // 存取器往往用于,属性的值依赖对象内部数据的场合
  26. var obj ={
  27. $n : 5,
  28. get next() { return this.$n++ },
  29. set next(n) {
  30. if (n >= this.$n) this.$n = n;
  31. else throw new Error('新的值必须大于当前值');
  32. }
  33. };
  34. obj.next // 5
  35. obj.next = 10;
  36. obj.next // 10
  37. obj.next = 5;
  38. // Uncaught Error: 新的值必须大于当前值
  39. // 上面代码中,next属性的存值函数和取值函数,都依赖于内部属性$n

对象的拷贝

  1. // 将一个对象的所有属性,拷贝到另一个对象,可以用下面的方法实现
  2. var extend = function (to, from) {
  3. for (var property in from) {
  4. to[property] = from[property];
  5. }
  6. return to;
  7. }
  8. extend({}, {
  9. a: 1
  10. })
  11. // {a: 1}
  12. // 上面这个方法的问题在于,如果遇到存取器定义的属性,会只拷贝值
  13. extend({}, {
  14. get a() { return 1 }
  15. })
  16. // {a: 1}
  17. // 为了解决这个问题,我们可以通过Object.defineProperty方法来拷贝属性
  18. var extend = function (to, from) {
  19. for (var property in from) {
  20. if (!from.hasOwnProperty(property)) continue;
  21. Object.defineProperty(
  22. to,
  23. property,
  24. Object.getOwnPropertyDescriptor(from, property)
  25. );
  26. }
  27. return to;
  28. }
  29. extend({}, { get a(){ return 1 } })
  30. // { get a(){ return 1 } })

冻结对象的读写状态,防止对象被改变,有三种冻结方式

  • 最弱的一种是Object.preventExtensions
  • 其次是Object.seal
  • 最强的是Object.freeze
  1. // Object.preventExtensions方法可以使得一个对象无法再添加新的属性
  2. var obj = new Object();
  3. Object.preventExtensions(obj);
  4. Object.defineProperty(obj, 'p', {
  5. value: 'hello'
  6. });
  7. // TypeError: Cannot define property:p, object is not extensible.
  8. obj.p = 1;
  9. obj.p // undefined
  10. // Object.isExtensible方法用于检查一个对象是否使用了Object.preventExtensions方法
  11. // 也就是说,检查是否可以为一个对象添加属性
  12. var obj = new Object();
  13. Object.isExtensible(obj) // true
  14. Object.preventExtensions(obj);
  15. Object.isExtensible(obj) // false
  16. // Object.seal方法使得一个对象既无法添加新属性,也无法删除旧属性
  17. var obj = { p: 'hello' };
  18. Object.seal(obj);
  19. delete obj.p;
  20. obj.p // "hello"
  21. obj.x = 'world';
  22. obj.x // undefined
  23. // Object.seal实质是把属性描述对象的configurable属性设为false,因此属性描述对象不再能改变了
  24. // Object.seal只是禁止新增或删除属性,并不影响修改某个属性的值
  25. // Object.isSealed方法用于检查一个对象是否使用了Object.seal方法
  26. var obj = { p: 'a' };
  27. Object.seal(obj);
  28. Object.isSealed(obj) // true
  29. // 这时,Object.isExtensible方法也返回false
  30. var obj = { p: 'a' };
  31. Object.seal(obj);
  32. Object.isExtensible(obj) // false
  33. // Object.freeze方法可以使得一个对象无法添加新属性、无法删除旧属性
  34. // 也无法改变属性的值,使得这个对象实际上变成了常量
  35. var obj = {
  36. p: 'hello'
  37. };
  38. Object.freeze(obj);
  39. obj.p = 'world';
  40. obj.p // "hello"
  41. obj.t = 'hello';
  42. obj.t // undefined
  43. delete obj.p // false
  44. obj.p // "hello"
  45. // 上面代码中,对obj对象进行Object.freeze()以后,修改属性、新增属性、删除属性都无效了
  46. // 这些操作并不报错,只是默默地失败,如果在严格模式下,则会报错
  47. // Object.isFrozen方法用于检查一个对象是否使用了Object.freeze方法
  48. var obj = {
  49. p: 'hello'
  50. };
  51. Object.freeze(obj);
  52. Object.isFrozen(obj) // true
  53. // 使用Object.freeze方法以后,Object.isSealed将会返回true,Object.isExtensible返回false
  54. var obj = {
  55. p: 'hello'
  56. };
  57. Object.freeze(obj);
  58. Object.isSealed(obj) // true
  59. Object.isExtensible(obj) // false
  60. // Object.isFrozen的一个用途是,确认某个对象没有被冻结后,再对它的属性赋值
  61. var obj = {
  62. p: 'hello'
  63. };
  64. Object.freeze(obj);
  65. if (!Object.isFrozen(obj)) {
  66. obj.p = 'world';
  67. }
  68. // 局限性
  69. // 上面的三个方法锁定对象的可写性有一个漏洞:可以通过改变原型对象,来为对象增加属性
  70. var obj = new Object();
  71. Object.preventExtensions(obj);
  72. var proto = Object.getPrototypeOf(obj);
  73. proto.t = 'hello';
  74. obj.t
  75. // hello
  76. // 对象obj本身不能新增属性,但是可以在它的原型对象上新增属性,就依然能够在obj上读到
  77. // 另外一个局限是,如果属性值是对象
  78. // 上面这些方法只能冻结属性指向的对象,而不能冻结对象本身的内容
  79. var obj = {
  80. foo: 1,
  81. bar: ['a', 'b']
  82. };
  83. Object.freeze(obj);
  84. obj.bar.push('c');
  85. obj.bar // ["a", "b", "c"]
  86. // 上面代码中,obj.bar属性指向一个数组,obj对象被冻结以后
  87. // 这个指向无法改变,即无法指向其他值,但是所指向的数组是可以改变的

3. Array 对象

  1. var arr = new Array(2);
  2. arr.length // 2
  3. arr // [ empty x 2 ]
  4. // 生成一个两个成员的数组,每个位置都是空值
  5. var arr = new Array(2);
  6. // 等同于
  7. var arr = Array(2);
  8. // 考虑到语义性,以及与其他构造函数用户保持一致,建议总是加上new
  9. // rray()作为构造函数,行为很不一致
  10. 因此,不建议使用它生成新数组,直接使用数组字面量是更好的做法
  11. // bad
  12. var arr = new Array(1, 2);
  13. // good
  14. var arr = [1, 2];
  15. var a = new Array(3);
  16. var b = [undefined, undefined, undefined];
  17. a.length // 3
  18. b.length // 3
  19. a[0] // undefined
  20. b[0] // undefined
  21. 0 in a // false
  22. 0 in b // true

Array.isArray()

  • Array.isArray方法返回一个布尔值,表示参数是否为数组。它可以弥补typeof运算符的不足
  1. var arr = [1, 2, 3];
  2. typeof arr // "object"
  3. Array.isArray(arr) // true
  4. // typeof运算符只能显示数组的类型是Object,而Array.isArray方法可以识别数组

valueOf(),toString()

  1. // 数组的valueOf方法返回数组本身
  2. var arr = [1, 2, 3];
  3. arr.valueOf() // [1, 2, 3]
  4. // 数组的toString方法返回数组的字符串形式
  5. var arr = [1, 2, 3];
  6. arr.toString() // "1,2,3"
  7. var arr = [1, 2, 3, [4, 5, 6]];
  8. arr.toString() // "1,2,3,4,5,6"

push(),pop()

  1. // push方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度
  2. // 注意,该方法会改变原数组
  3. var arr = [];
  4. arr.push(1) // 1
  5. arr.push('a') // 2
  6. arr.push(true, {}) // 4
  7. arr // [1, 'a', true, {}]
  8. // pop方法用于删除数组的最后一个元素,并返回该元素
  9. // 注意,该方法会改变原数组
  10. var arr = ['a', 'b', 'c'];
  11. arr.pop() // 'c'
  12. arr // ['a', 'b']
  13. // 对空数组使用pop方法,不会报错,而是返回undefined
  14. [].pop() // undefined
  15. // push和pop结合使用,就构成了“后进先出”的栈结构(stack)
  16. var arr = [];
  17. arr.push(1, 2);
  18. arr.push(3);
  19. arr.pop();
  20. arr // [1, 2]

shift(),unshift()

  1. // shift()方法用于删除数组的第一个元素,并返回该元素
  2. // 注意,该方法会改变原数组
  3. var a = ['a', 'b', 'c'];
  4. a.shift() // 'a'
  5. a // ['b', 'c']
  6. // shift()方法可以遍历并清空一个数组
  7. var list = [1, 2, 3, 4];
  8. var item;
  9. while (item = list.shift()) {
  10. console.log(item);
  11. }
  12. list // []
  13. // 它的前提是数组元素不能是0或任何布尔值等于false的元素
  14. // 因此这样的遍历不是很可靠
  15. // push()和shift()结合使用,就构成了“先进先出”的队列结构(queue)
  16. // unshift()方法用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度
  17. 注意,该方法会改变原数组
  18. var a = ['a', 'b', 'c'];
  19. a.unshift('x'); // 4
  20. a // ['x', 'a', 'b', 'c']
  21. // unshift()方法可以接受多个参数,这些参数都会添加到目标数组头部
  22. var arr = [ 'c', 'd' ];
  23. arr.unshift('a', 'b') // 4
  24. arr // [ 'a', 'b', 'c', 'd' ]

join()

  1. // join()方法以指定参数作为分隔符,将所有数组成员连接为一个字符串返回
  2. 如果不提供参数,默认用逗号分隔
  3. var a = [1, 2, 3, 4];
  4. a.join(' ') // '1 2 3 4'
  5. a.join(' | ') // "1 | 2 | 3 | 4"
  6. a.join() // "1,2,3,4"
  7. // 如果数组成员是undefined或null或空位,会被转成空字符串
  8. [undefined, null].join('#')
  9. // '#'
  10. ['a',, 'b'].join('-')
  11. // 'a--b'
  12. // 通过call方法,这个方法也可以用于字符串或类似数组的对象
  13. Array.prototype.join.call('hello', '-')
  14. // "h-e-l-l-o"
  15. var obj = { 0: 'a', 1: 'b', length: 2 };
  16. Array.prototype.join.call(obj, '-')
  17. // 'a-b'

concat()

  1. // concat方法用于多个数组的合并
  2. // 它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变
  3. ['hello'].concat(['world'])
  4. // ["hello", "world"]
  5. ['hello'].concat(['world'], ['!'])
  6. // ["hello", "world", "!"]
  7. [].concat({a: 1}, {b: 2})
  8. // [{ a: 1 }, { b: 2 }]
  9. [2].concat({a: 1})
  10. // [2, {a: 1}]
  11. // 除了数组作为参数,concat也接受其他类型的值作为参数,添加到目标数组尾部。
  12. [1, 2, 3].concat(4, 5, 6)
  13. // [1, 2, 3, 4, 5, 6]
  14. // 如果数组成员包括对象,concat方法返回当前数组的一个浅拷贝
  15. var obj = { a: 1 };
  16. var oldArray = [obj];
  17. var newArray = oldArray.concat();
  18. obj.a = 2;
  19. newArray[0].a // 2

reverse()

  1. // reverse方法用于颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组
  2. var a = ['a', 'b', 'c'];
  3. a.reverse() // ["c", "b", "a"]
  4. a // ["c", "b", "a"]

slice()

  1. // slice()方法用于提取目标数组的一部分,返回一个新数组,原数组不变
  2. arr.slice(start, end);
  3. var a = ['a', 'b', 'c'];
  4. a.slice(0) // ["a", "b", "c"]
  5. a.slice(1) // ["b", "c"]
  6. a.slice(1, 2) // ["b"]
  7. a.slice(2, 6) // ["c"]
  8. a.slice() // ["a", "b", "c"]
  9. // 最后一个例子slice()没有参数,实际上等于返回一个原数组的拷贝
  10. // 如果slice()方法的参数是负数,则表示倒数计算的位置
  11. var a = ['a', 'b', 'c'];
  12. a.slice(-2) // ["b", "c"]
  13. a.slice(-2, -1) // ["b"]
  14. // 如果第一个参数大于等于数组长度,或者第二个参数小于第一个参数,则返回空数组
  15. var a = ['a', 'b', 'c'];
  16. a.slice(4) // []
  17. a.slice(2, 1) // []
  18. // slice()方法的一个重要应用,是将类似数组的对象转为真正的数组
  19. Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
  20. // ['a', 'b']
  21. Array.prototype.slice.call(document.querySelectorAll("div"));
  22. Array.prototype.slice.call(arguments);

splice()

  1. // splice()方法用于删除原数组的一部分成员
  2. // 并可以在删除的位置添加新的数组成员,返回值是被删除的元素
  3. // 注意,该方法会改变原数组
  4. arr.splice(start, count, addElement1, addElement2, ...);
  5. var a = ['a', 'b', 'c', 'd', 'e', 'f'];
  6. a.splice(4, 2) // ["e", "f"]
  7. a // ["a", "b", "c", "d"]
  8. // 起始位置如果是负数,就表示从倒数位置开始删除
  9. var a = ['a', 'b', 'c', 'd', 'e', 'f'];
  10. a.splice(-4, 2) // ["c", "d"]
  11. // 如果只是单纯地插入元素,splice方法的第二个参数可以设为0
  12. var a = [1, 1, 1];
  13. a.splice(1, 0, 2) // []
  14. a // [1, 2, 1, 1]
  15. // 如果只提供第一个参数,等同于将原数组在指定位置拆分成两个数组
  16. var a = [1, 2, 3, 4];
  17. a.splice(2) // [3, 4]
  18. a // [1, 2]

sort()

  1. // sort方法对数组成员进行排序,默认是按照字典顺序排序
  2. // 排序后,原数组将被改变
  3. ['d', 'c', 'b', 'a'].sort()
  4. // ['a', 'b', 'c', 'd']
  5. [4, 3, 2, 1].sort()
  6. // [1, 2, 3, 4]
  7. [11, 101].sort()
  8. // [101, 11]
  9. [10111, 1101, 111].sort()
  10. // [10111, 1101, 111]
  11. // 上面代码的最后两个例子,需要特殊注意
  12. // sort()方法不是按照大小排序,而是按照字典顺序
  13. // 也就是说,数值会被先转成字符串,再按照字典顺序进行比较,所以101排在11的前面
  14. // 如果想让sort方法按照自定义方式排序,可以传入一个函数作为参数
  15. [10111, 1101, 111].sort(function (a, b) {
  16. return a - b;
  17. })
  18. // [111, 1101, 10111]
  19. // 上面代码中,sort的参数函数本身接受两个参数,表示进行比较的两个数组成员
  20. [
  21. { name: "张三", age: 30 },
  22. { name: "李四", age: 24 },
  23. { name: "王五", age: 28 }
  24. ].sort(function (o1, o2) {
  25. return o1.age - o2.age;
  26. })
  27. // [
  28. // { name: "李四", age: 24 },
  29. // { name: "王五", age: 28 },
  30. // { name: "张三", age: 30 }
  31. // ]
  32. // bad
  33. [1, 4, 2, 6, 0, 6, 2, 6].sort((a, b) => a > b)
  34. // good
  35. [1, 4, 2, 6, 0, 6, 2, 6].sort((a, b) => a - b)
  36. // 前一种排序算法返回的是布尔值,这是不推荐使用的。后一种是数值,才是更好的写法

map()

  1. // map方法将数组的所有成员依次传入参数函数
  2. // 然后把每一次的执行结果组成一个新数组返回
  3. var numbers = [1, 2, 3];
  4. numbers.map(function (n) {
  5. return n + 1;
  6. });
  7. // [2, 3, 4]
  8. numbers
  9. // [1, 2, 3]
  10. // map方法接受一个函数作为参数
  11. // 该函数调用时,map方法向它传入三个参数:当前成员、当前位置和数组本身
  12. [1, 2, 3].map(function(elem, index, arr) {
  13. return elem * index;
  14. });
  15. // [0, 2, 6]
  16. // map方法还可以接受第二个参数,用来绑定回调函数内部的this变量
  17. var arr = ['a', 'b', 'c'];
  18. [1, 2].map(function (e) {
  19. return this[e];
  20. }, arr)
  21. // ['b', 'c']
  22. // 如果数组有空位,map方法的回调函数在这个位置不会执行,会跳过数组的空位
  23. var f = function (n) { return 'a' };
  24. [1, undefined, 2].map(f) // ["a", "a", "a"]
  25. [1, null, 2].map(f) // ["a", "a", "a"]
  26. [1, , 2].map(f) // ["a", , "a"]

forEach()

  1. // forEach方法与map方法很相似,也是对数组的所有成员依次执行参数函数
  2. // 但是,forEach方法不返回值,只用来操作数据
  3. function log(element, index, array) {
  4. console.log('[' + index + '] = ' + element);
  5. }
  6. [2, 5, 9].forEach(log);
  7. // [0] = 2
  8. // [1] = 5
  9. // [2] = 9
  10. // forEach方法也可以接受第二个参数,绑定参数函数的this变量
  11. var out = [];
  12. [1, 2, 3].forEach(function(elem) {
  13. this.push(elem * elem);
  14. }, out);
  15. out // [1, 4, 9]
  16. // 注意,forEach方法无法中断执行,总是会将所有成员遍历完
  17. // 如果希望符合某种条件时,就中断遍历,要使用for循环
  18. var arr = [1, 2, 3];
  19. for (var i = 0; i < arr.length; i++) {
  20. if (arr[i] === 2) break;
  21. console.log(arr[i]);
  22. }
  23. // 1
  24. // forEach方法也会跳过数组的空位
  25. var log = function (n) {
  26. console.log(n + 1);
  27. };
  28. [1, undefined, 2].forEach(log)
  29. // 2
  30. // NaN
  31. // 3
  32. [1, null, 2].forEach(log)
  33. // 2
  34. // 1
  35. // 3
  36. [1, , 2].forEach(log)
  37. // 2
  38. // 3

filter()

  1. // filter方法用于过滤数组成员,满足条件的成员组成一个新数组返回
  2. [1, 2, 3, 4, 5].filter(function (elem) {
  3. return (elem > 3);
  4. })
  5. // [4, 5]
  6. var arr = [0, 1, 'a', false];
  7. arr.filter(Boolean)
  8. // [1, "a"]
  9. // filter方法的参数函数可以接受三个参数:当前成员,当前位置和整个数组
  10. [1, 2, 3, 4, 5].filter(function (elem, index, arr) {
  11. return index % 2 === 0;
  12. });
  13. // [1, 3, 5]
  14. // filter方法还可以接受第二个参数,用来绑定参数函数内部的this变量
  15. var obj = { MAX: 3 };
  16. var myFilter = function (item) {
  17. if (item > this.MAX) return true;
  18. };
  19. var arr = [2, 8, 3, 4, 1, 3, 2, 9];
  20. arr.filter(myFilter, obj) // [8, 4, 9]

some(),every()

  1. //这两个方法类似“断言”(assert),返回一个布尔值
  2. // 表示判断数组成员是否符合某种条件
  3. // 它们接受一个函数作为参数,所有数组成员依次执行该函数
  4. // 该函数接受三个参数:当前成员、当前位置和整个数组,然后返回一个布尔值
  5. // some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true
  6. // 否则返回false
  7. var arr = [1, 2, 3, 4, 5];
  8. arr.some(function (elem, index, arr) {
  9. return elem >= 3;
  10. });
  11. // true
  12. // every方法是所有成员的返回值都是true,整个every方法才返回true
  13. // 否则返回false
  14. var arr = [1, 2, 3, 4, 5];
  15. arr.every(function (elem, index, arr) {
  16. return elem >= 3;
  17. });
  18. // false
  19. // 注意,对于空数组,some方法返回false
  20. // every方法返回true
  21. // 回调函数都不会执行
  22. function isEven(x) { return x % 2 === 0 }
  23. [].some(isEven) // false
  24. [].every(isEven) // true
  25. // some和every方法还可以接受第二个参数,用来绑定参数函数内部的this变量

reduce(),reduceRight()

  1. // reduce方法和reduceRight方法依次处理数组的每个成员,最终累计为一个值
  2. // 它们的差别是,reduce是从左到右处理(从第一个成员到最后一个成员)
  3. // reduceRight则是从右到左(从最后一个成员到第一个成员),其他完全一样
  4. [1, 2, 3, 4, 5].reduce(function (a, b) {
  5. console.log(a, b);
  6. return a + b;
  7. })
  8. // 1 2
  9. // 3 3
  10. // 6 4
  11. // 10 5
  12. //最后结果:15
  13. // reduce方法和reduceRight方法的第一个参数都是一个函数
  14. // 该函数接受以下四个参数。
  15. 累积变量,默认为数组的第一个成员
  16. 当前变量,默认为数组的第二个成员
  17. 当前位置(从0开始)
  18. 原数组
  19. // 这四个参数之中,只有前两个是必须的,后两个则是可选的
  20. // 如果要对累积变量指定初值,可以把它放在reduce方法和reduceRight方法的第二个参数
  21. [1, 2, 3, 4, 5].reduce(function (a, b) {
  22. return a + b;
  23. }, 10);
  24. // 25
  25. // 上面的第二个参数相当于设定了默认值,处理空数组时尤其有用
  26. function add(prev, cur) {
  27. return prev + cur;
  28. }
  29. [].reduce(add)
  30. // TypeError: Reduce of empty array with no initial value
  31. [].reduce(add, 1)
  32. // 1
  33. // reduceRight方法的例子
  34. function subtract(prev, cur) {
  35. return prev - cur;
  36. }
  37. [3, 2, 1].reduce(subtract) // 0
  38. [3, 2, 1].reduceRight(subtract) // -4
  39. // 上面代码中,reduce方法相当于3减去2再减去1
  40. // reduceRight方法相当于1减去2再减去3
  41. // 由于这两个方法会遍历数组,所以实际上还可以用来做一些遍历相关的操作
  42. // 比如,找出字符长度最长的数组成员
  43. function findLongest(entries) {
  44. return entries.reduce(function (longest, entry) {
  45. return entry.length > longest.length ? entry : longest;
  46. }, '');
  47. }
  48. findLongest(['aaa', 'bb', 'c']) // "aaa"
  49. // reduce的参数函数会将字符长度较长的那个数组成员,作为累积值
  50. // 这导致遍历所有成员之后,累积值就是字符长度最长的那个成员

indexOf(),lastIndexOf()

  1. // indexOf方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1
  2. var a = ['a', 'b', 'c'];
  3. a.indexOf('b') // 1
  4. a.indexOf('y') // -1
  5. // indexOf方法还可以接受第二个参数,表示搜索的开始位置
  6. ['a', 'b', 'c'].indexOf('a', 1) // -1
  7. // lastIndexOf方法返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1
  8. var a = [2, 5, 9, 2];
  9. a.lastIndexOf(2) // 3
  10. a.lastIndexOf(7) // -1
  11. // 注意,这两个方法不能用来搜索NaN的位置,即它们无法确定数组成员是否包含NaN
  12. [NaN].indexOf(NaN) // -1
  13. [NaN].lastIndexOf(NaN) // -1
  14. // 这是因为这两个方法内部,使用严格相等运算符(===)进行比较
  15. // 而NaN是唯一一个不等于自身的值

链式使用

  1. var users = [
  2. {name: 'tom', email: 'tom@example.com'},
  3. {name: 'peter', email: 'peter@example.com'}
  4. ];
  5. users
  6. .map(function (user) {
  7. return user.email;
  8. })
  9. .filter(function (email) {
  10. return /^t/.test(email);
  11. })
  12. .forEach(function (email) {
  13. console.log(email);
  14. });
  15. // "tom@example.com"

4. 包装对象

  • 所谓“包装对象”,指的是与数值、字符串、布尔值分别相对应的Number、String、Boolean三个原生对象
  • 这三个原生对象可以把原始类型的值变成(包装成)对象
  • 包装对象的设计目的
    • 首先是使得“对象”这种类型可以覆盖 JavaScript 所有的值,整门语言有一个通用的数据模型
    • 其次是使得原始类型的值也有办法调用自己的方法
  1. var v1 = new Number(123);
  2. var v2 = new String('abc');
  3. var v3 = new Boolean(true);
  4. typeof v1 // "object"
  5. typeof v2 // "object"
  6. typeof v3 // "object"
  7. v1 === 123 // false
  8. v2 === 'abc' // false
  9. v3 === true // false
  10. // Number、String和Boolean这三个原生对象
  11. // 如果不作为构造函数调用(即调用时不加new),而是作为普通函数调用
  12. // 常常用于将任意类型的值转为数值、字符串和布尔值
  13. // 字符串转为数值
  14. Number('123') // 123
  15. // 数值转为字符串
  16. String(123) // "123"
  17. // 数值转为布尔值
  18. Boolean(123) // true
  19. // 实例方法
  20. valueOf()方法返回包装对象实例对应的原始类型的值
  21. new Number(123).valueOf() // 123
  22. new String('abc').valueOf() // "abc"
  23. new Boolean(true).valueOf() // true
  24. // toString()方法返回对应的字符串形式
  25. new Number(123).toString() // "123"
  26. new String('abc').toString() // "abc"
  27. new Boolean(true).toString() // "true"

原始类型与实例对象的自动转换

  • 某些场合,原始类型的值会自动当作包装对象调用,即调用包装对象的属性和方法
  • 这时,JavaScript 引擎会自动将原始类型的值转为包装对象实例,并在使用后立刻销毁实例
  1. 'abc'.length // 3
  2. var str = 'abc';
  3. str.length // 3
  4. // 等同于
  5. var strObj = new String(str)
  6. // String {
  7. // 0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"
  8. // }
  9. strObj.length // 3
  10. // 上面代码中,字符串abc的包装对象提供了多个属性,length只是其中之一
  11. // 自动转换生成的包装对象是只读的,无法修改。所以,字符串无法添加新属性
  12. var s = 'Hello World';
  13. s.x = 123;
  14. s.x // undefined

自定义方法

  1. String.prototype.double = function () {
  2. return this.valueOf() + this.valueOf();
  3. };
  4. 'abc'.double()
  5. // abcabc
  6. Number.prototype.double = function () {
  7. return this.valueOf() + this.valueOf();
  8. };
  9. (123).double() // 246
  10. // 注意,最后一行的123外面必须要加上圆括号
  11. // 否则后面的点运算符(.)会被解释成小数点

5. Boolean 对象

  • Boolean对象是 JavaScript 的三个包装对象之一
  • 作为构造函数,它主要用于生成布尔值的包装对象实例
  1. var b = new Boolean(true);
  2. typeof b // "object"
  3. b.valueOf() // true
  4. // 注意,false对应的包装对象实例,布尔运算结果也是true
  5. if (new Boolean(false)) {
  6. console.log('true');
  7. } // true
  8. if (new Boolean(false).valueOf()) {
  9. console.log('true');
  10. } // 无输出

Boolean 函数的类型转换作用

  1. Boolean(undefined) // false
  2. Boolean(null) // false
  3. Boolean(0) // false
  4. Boolean('') // false
  5. Boolean(NaN) // false
  6. Boolean(1) // true
  7. Boolean('false') // true
  8. Boolean([]) // true
  9. Boolean({}) // true
  10. Boolean(function () {}) // true
  11. Boolean(/foo/) // true
  12. // 使用双重的否运算符(!)也可以将任意值转为对应的布尔值
  13. !!undefined // false
  14. !!null // false
  15. !!0 // false
  16. !!'' // false
  17. !!NaN // false
  18. !!1 // true
  19. !!'false' // true
  20. !![] // true
  21. !!{} // true
  22. !!function(){} // true
  23. !!/foo/ // true
  24. // 对于一些特殊值,Boolean对象前面加不加new,会得到完全相反的结果,必须小心
  25. if (Boolean(false)) {
  26. console.log('true');
  27. } // 无输出
  28. if (new Boolean(false)) {
  29. console.log('true');
  30. } // true
  31. if (Boolean(null)) {
  32. console.log('true');
  33. } // 无输出
  34. if (new Boolean(null)) {
  35. console.log('true');
  36. } // true

6. Number 对象

  • Number对象是数值对应的包装对象
    • 可以作为构造函数使用
    • 也可以作为工具函数使用
  1. var n = new Number(1);
  2. typeof n // "object"
  3. Number(true) // 1

静态属性

  1. Number.POSITIVE_INFINITY // Infinity
  2. Number.NEGATIVE_INFINITY // -Infinity
  3. Number.NaN // NaN
  4. Number.MAX_VALUE
  5. // 1.7976931348623157e+308
  6. Number.MAX_VALUE < Infinity
  7. // true
  8. Number.MIN_VALUE
  9. // 5e-324
  10. Number.MIN_VALUE > 0
  11. // true
  12. Number.MAX_SAFE_INTEGER // 9007199254740991
  13. Number.MIN_SAFE_INTEGER // -9007199254740991

实例方法

  1. // Number.prototype.toString()
  2. // Number对象部署了自己的toString方法,用来将一个数值转为字符串形式
  3. (10).toString() // "10"
  4. // toString方法可以接受一个参数,表示输出的进制
  5. // 如果省略这个参数,默认将数值先转为十进制
  6. (10).toString(2) // "1010"
  7. (10).toString(8) // "12"
  8. (10).toString(16) // "a"
  9. // toString方法只能将十进制的数,转为其他进制的字符串
  10. // 如果要将其他进制的数,转回十进制,需要使用parseInt方法
  11. // Number.prototype.toFixed()
  12. // toFixed()方法先将一个数转为指定位数的小数,然后返回这个小数对应的字符串
  13. (10).toFixed(2) // "10.00"
  14. 10.005.toFixed(2) // "10.01"
  15. // 由于浮点数的原因,小数5的四舍五入是不确定的,使用的时候必须小心
  16. // Number.prototype.toExponential()
  17. // toExponential方法用于将一个数转为科学计数法形式
  18. (10).toExponential() // "1e+1"
  19. (10).toExponential(1) // "1.0e+1"
  20. (10).toExponential(2) // "1.00e+1"
  21. (1234).toExponential() // "1.234e+3"
  22. (1234).toExponential(1) // "1.2e+3"
  23. (1234).toExponential(2) // "1.23e+3"
  24. // toExponential方法的参数是小数点后有效数字的位数,范围为0到100
  25. // 超出这个范围,会抛出一个 RangeError 错误
  26. // Number.prototype.toPrecision()
  27. // Number.prototype.toPrecision()方法用于将一个数转为指定位数的有效数字
  28. (12.34).toPrecision(1) // "1e+1"
  29. (12.34).toPrecision(2) // "12"
  30. (12.34).toPrecision(3) // "12.3"
  31. (12.34).toPrecision(4) // "12.34"
  32. (12.34).toPrecision(5) // "12.340"
  33. // 该方法用于四舍五入时不太可靠,跟浮点数不是精确储存有
  34. (12.35).toPrecision(3) // "12.3"
  35. (12.25).toPrecision(3) // "12.3"
  36. (12.15).toPrecision(3) // "12.2"
  37. (12.45).toPrecision(3) // "12.4"
  38. // Number.prototype.toLocaleString()
  39. // Number.prototype.toLocaleString()方法接受一个地区码作为参数,返回一个字符串
  40. // 表示当前数字在该地区的当地书写形式
  41. (123).toLocaleString('zh-Hans-CN-u-nu-hanidec')
  42. // "一二三"
  43. // 该方法还可以接受第二个参数配置对象,用来定制指定用途的返回字符串
  44. // 该对象的style属性指定输出样式,默认值是decimal,表示输出十进制形式
  45. // 如果值为percent,表示输出百分数
  46. (123).toLocaleString('zh-Hans-CN', { style: 'percent' })
  47. // "12,300%"
  48. // 如果style属性的值为currency,则可以搭配currency属性,输出指定格式的货币字符串形式
  49. (123).toLocaleString('zh-Hans-CN', { style: 'currency', currency: 'CNY' })
  50. // "¥123.00"
  51. (123).toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })
  52. // "123,00 €"
  53. (123).toLocaleString('en-US', { style: 'currency', currency: 'USD' })
  54. // "$123.00"
  55. // 如果Number.prototype.toLocaleString()省略了参数,则由浏览器自行决定如何处理
  56. // 注意,该方法如果使用浏览器不认识的地区码,会抛出一个错误
  57. (123).toLocaleString('123') // 出错

自定义方法

  1. Number.prototype.add = function (x) {
  2. return this + x;
  3. };
  4. 8['add'](2) // 10
  5. // 链式运算
  6. Number.prototype.subtract = function (x) {
  7. return this - x;
  8. };
  9. (8).add(2).subtract(4)
  10. // 6
  11. // 部署更复杂的运算
  12. Number.prototype.iterate = function () {
  13. var result = [];
  14. for (var i = 0; i <= this; i++) {
  15. result.push(i);
  16. }
  17. return result;
  18. };
  19. (8).iterate()
  20. // [0, 1, 2, 3, 4, 5, 6, 7, 8]
  21. // 注意,数值的自定义方法,只能定义在它的原型对象Number.prototype上面
  22. // 数值本身是无法自定义属性的
  23. var n = 1;
  24. n.x = 1;
  25. n.x // undefined

7. String 对象

  • String对象是 JavaScript 原生提供的三个包装对象之一
  • 用来生成字符串对象
  1. var s1 = 'abc';
  2. var s2 = new String('abc');
  3. typeof s1 // "string"
  4. typeof s2 // "object"
  5. s2.valueOf() // "abc"
  6. // 字符串对象是一个类似数组的对象(很像数组,但不是数组)
  7. new String('abc')
  8. // String {0: "a", 1: "b", 2: "c", length: 3}
  9. (new String('abc'))[1] // "b"
  10. // String对象还可以当作工具方法使用,将任意类型的值转为字符串
  11. String(true) // "true"
  12. String(5) // "5"
  13. // 静态方法
  14. // String.fromCharCode()
  15. // 该方法的参数是一个或多个数值,代表 Unicode 码点,返回值是这些码点组成的字符串
  16. String.fromCharCode() // ""
  17. String.fromCharCode(97) // "a"
  18. String.fromCharCode(104, 101, 108, 108, 111)
  19. // "hello"
  20. // 注意,该方法不支持 Unicode 码点大于0xFFFF的字符
  21. // 即传入的参数不能大于0xFFFF(即十进制的 65535)
  22. // JavaScript 默认支持两个字节的字符,这种情况下,必须把0x20BB7拆成两个字符表示
  23. String.fromCharCode(0xD842, 0xDFB7)
  24. // "𠮷"

实例属性和方法

  1. // length
  2. 'abc'.length // 3
  3. // charAt方法返回指定位置的字符,参数是从0开始编号的位置
  4. var s = new String('abc');
  5. s.charAt(1) // "b"
  6. s.charAt(s.length - 1) // "c"
  7. // 这个方法完全可以用数组下标替代
  8. 'abc'.charAt(1) // "b"
  9. 'abc'[1] // "b"
  10. // charCodeAt()方法返回字符串指定位置的 Unicode 码点(十进制表示)
  11. // 相当于String.fromCharCode()的逆操作
  12. 'abc'.charCodeAt(1) // 98
  13. // 如果没有任何参数,charCodeAt返回首字符的 Unicode 码点
  14. 'abc'.charCodeAt() // 97
  15. // 如果参数为负数,或大于等于字符串的长度,charCodeAt返回NaN
  16. 'abc'.charCodeAt(-1) // NaN
  17. 'abc'.charCodeAt(4) // NaN
  18. // 注意,charCodeAt方法返回的 Unicode 码点不会大于65536(0xFFFF)
  19. // concat方法用于连接两个字符串,返回一个新字符串,不改变原字符串
  20. var s1 = 'abc';
  21. var s2 = 'def';
  22. s1.concat(s2) // "abcdef"
  23. s1 // "abc
  24. // 该方法可以接受多个参数
  25. 'a'.concat('b', 'c') // "abc"
  26. // 如果参数不是字符串,concat方法会将其先转为字符串,然后再连接
  27. var one = 1;
  28. var two = 2;
  29. var three = '3';
  30. ''.concat(one, two, three) // "123"
  31. one + two + three // "33"
  32. // 作为对比,加号运算符在两个运算数都是数值时,不会转换类型
  33. // 所以返回的是一个两个字符的字符串
  34. // slice()方法用于从原字符串取出子字符串并返回,不改变原字符串
  35. 'JavaScript'.slice(0, 4) // "Java"
  36. 'JavaScript'.slice(4) // "Script"
  37. 'JavaScript'.slice(-6) // "Script"
  38. 'JavaScript'.slice(0, -6) // "Java"
  39. 'JavaScript'.slice(-2, -1) // "p"
  40. 'JavaScript'.slice(2, 1) // ""
  41. // substring方法用于从原字符串取出子字符串并返回,不改变原字符串
  42. // 跟slice方法很相像
  43. 'JavaScript'.substring(0, 4) // "Java"
  44. 'JavaScript'.substring(4) // "Script"
  45. // 如果第一个参数大于第二个参数,substring方法会自动更换两个参数的位置
  46. 'JavaScript'.substring(10, 4) // "Script"
  47. // 等同于
  48. 'JavaScript'.substring(4, 10) // "Script"
  49. // 如果参数是负数,substring方法会自动将负数转为0
  50. 'JavaScript'.substring(-3) // "JavaScript"
  51. 'JavaScript'.substring(4, -3) // "Java"
  52. // 由于这些规则违反直觉,因此不建议使用substring方法,应该优先使用slice
  53. // substr方法用于从原字符串取出子字符串并返回,不改变原字符串
  54. // 跟slice和substring方法的作用相同
  55. 'JavaScript'.substr(4, 6) // "Script"
  56. 'JavaScript'.substr(4) // "Script"
  57. 'JavaScript'.substr(-6) // "Script"
  58. 'JavaScript'.substr(4, -1) // ""
  59. // indexOf方法用于确定一个字符串在另一个字符串中第一次出现的位置
  60. // 返回结果是匹配开始的位置
  61. // 如果返回-1,就表示不匹配
  62. 'hello world'.indexOf('o') // 4
  63. 'JavaScript'.indexOf('script') // -1
  64. 'hello world'.indexOf('o', 6) // 7
  65. // lastIndexOf方法的用法跟indexOf方法一致
  66. // 主要的区别是lastIndexOf从尾部开始匹配,indexOf则是从头部开始匹配
  67. 'hello world'.lastIndexOf('o') // 7
  68. lastIndexOf的第二个参数表示从该位置起向前匹配
  69. 'hello world'.lastIndexOf('o', 6) // 4
  70. // trim方法用于去除字符串两端的空格,返回一个新字符串,不改变原字符串
  71. ' hello world '.trim()
  72. // "hello world"
  73. // 该方法去除的不仅是空格,还包括制表符(\t、\v)、换行符(\n)和回车符(\r)
  74. // toLowerCase方法用于将一个字符串全部转为小写
  75. // toUpperCase则是全部转为大写
  76. // 它们都返回一个新字符串,不改变原字符串
  77. 'Hello World'.toLowerCase()
  78. // "hello world"
  79. 'Hello World'.toUpperCase()
  80. // "HELLO WORLD"
  81. match方法用于确定原字符串是否匹配某个子字符串,返回一个数组
  82. // 成员为匹配的第一个字符串
  83. 'cat, bat, sat, fat'.match('at') // ["at"]
  84. 'cat, bat, sat, fat'.match('xt') // null
  85. // 返回的数组还有index属性和input属性,分别表示匹配字符串开始的位置和原始字符串
  86. var matches = 'cat, bat, sat, fat'.match('at');
  87. matches.index // 1
  88. matches.input // "cat, bat, sat, fat"
  89. // match方法还可以使用正则表达式作为参数
  90. // search方法的用法基本等同于match,但是返回值为匹配的第一个位置
  91. // 如果没有找到匹配,则返回-1
  92. 'cat, bat, sat, fat'.search('at') // 1
  93. // search方法还可以使用正则表达式作为参数
  94. // replace方法用于替换匹配的子字符串
  95. //一般情况下只替换第一个匹配(除非使用带有g修饰符的正则表达式)
  96. 'aaa'.replace('a', 'b') // "baa"
  97. // replace方法还可以使用正则表达式作为参数
  98. split方法按照给定规则分割字符串,返回一个由分割出来的子字符串组成的数组
  99. 'a|b|c'.split('|') // ["a", "b", "c"]
  100. 'a|b|c'.split('') // ["a", "|", "b", "|", "c"]
  101. 'a|b|c'.split() // ["a|b|c"]
  102. 'a||c'.split('|') // ['a', '', 'c']
  103. '|b|c'.split('|') // ["", "b", "c"]
  104. 'a|b|'.split('|') // ["a", "b", ""]
  105. // split方法还可以接受第二个参数,限定返回数组的最大成员数
  106. 'a|b|c'.split('|', 0) // []
  107. 'a|b|c'.split('|', 1) // ["a"]
  108. 'a|b|c'.split('|', 2) // ["a", "b"]
  109. 'a|b|c'.split('|', 3) // ["a", "b", "c"]
  110. 'a|b|c'.split('|', 4) // ["a", "b", "c"]
  111. // split方法还可以使用正则表达式作为参数
  112. // localeCompare方法用于比较两个字符串,它返回一个整数
  113. // 如果小于0,表示第一个字符串小于第二个字符串
  114. // 如果等于0,表示两者相等
  115. // 如果大于0,表示第一个字符串大于第二个字符串
  116. 'apple'.localeCompare('banana') // -1
  117. 'apple'.localeCompare('apple') // 0
  118. // 该方法的最大特点,就是会考虑自然语言的顺序
  119. // 举例来说,正常情况下,大写的英文字母小于小写字母
  120. 'B' > 'a' // false

8. Math 对象

Math对象的静态属性,提供以下一些数学常数

  1. Math.E:常数e
  2. Math.LN22 的自然对数
  3. Math.LN1010 的自然对数
  4. Math.LOG2E:以 2 为底的e的对数
  5. Math.LOG10E:以 10 为底的e的对数
  6. Math.PI:常数π
  7. Math.SQRT1_20.5 的平方根
  8. Math.SQRT22 的平方根
  9. Math.E // 2.718281828459045
  10. Math.LN2 // 0.6931471805599453
  11. Math.LN10 // 2.302585092994046
  12. Math.LOG2E // 1.4426950408889634
  13. Math.LOG10E // 0.4342944819032518
  14. Math.PI // 3.141592653589793
  15. Math.SQRT1_2 // 0.7071067811865476
  16. Math.SQRT2 // 1.4142135623730951
  17. // 这些属性都是只读的,不能修改。

Math对象提供以下一些静态方法

  1. Math.abs():绝对值
  2. Math.ceil():向上取整
  3. Math.floor():向下取整
  4. Math.max():最大值
  5. Math.min():最小值
  6. Math.pow():幂运算
  7. Math.sqrt():平方根
  8. Math.log():自然对数
  9. Math.exp():e的指数
  10. Math.round():四舍五入
  11. Math.random():随机数
  12. // Math.abs方法返回参数值的绝对值
  13. Math.abs(1) // 1
  14. Math.abs(-1) // 1
  15. // Math.max方法返回参数之中最大的那个值
  16. // Math.min返回最小的那个值
  17. Math.max(2, -1, 5) // 5
  18. Math.min(2, -1, 5) // -1
  19. Math.min() // Infinity
  20. Math.max() // -Infinity
  21. // Math.floor方法返回小于参数值的最大整数(地板值)
  22. // Math.ceil方法返回大于参数值的最小整数(天花板值)
  23. Math.floor(3.2) // 3
  24. Math.floor(-3.2) // -4
  25. Math.ceil(3.2) // 4
  26. Math.ceil(-3.2) // -3
  27. // 这两个方法可以结合起来,实现一个总是返回数值的整数部分的函数
  28. function ToInteger(x) {
  29. x = Number(x);
  30. return x < 0 ? Math.ceil(x) : Math.floor(x);
  31. }
  32. ToInteger(3.2) // 3
  33. ToInteger(3.5) // 3
  34. ToInteger(3.8) // 3
  35. ToInteger(-3.2) // -3
  36. ToInteger(-3.5) // -3
  37. ToInteger(-3.8) // -3
  38. // Math.round方法用于四舍五入
  39. Math.round(0.1) // 0
  40. Math.round(0.5) // 1
  41. Math.round(0.6) // 1
  42. // 等同于
  43. Math.floor(x + 0.5)
  44. // 注意,它对负数的处理(主要是对0.5的处理)
  45. Math.round(-1.1) // -1
  46. Math.round(-1.5) // -1
  47. Math.round(-1.6) // -2
  48. Math.pow方法返回以第一个参数为底数、第二个参数为指数的幂运算值
  49. // 等同于 2 ** 2
  50. Math.pow(2, 2) // 4
  51. // 等同于 2 ** 3
  52. Math.pow(2, 3) // 8
  53. // 下面是计算圆面积的方法
  54. var radius = 20;
  55. var area = Math.PI * Math.pow(radius, 2);
  56. // Math.sqrt方法返回参数值的平方根。如果参数是一个负值,则返回NaN
  57. Math.sqrt(4) // 2
  58. Math.sqrt(-4) // NaN
  59. // Math.log方法返回以e为底的自然对数值
  60. Math.log(Math.E) // 1
  61. Math.log(10) // 2.302585092994046
  62. // 如果要计算以10为底的对数,可以先用Math.log求出自然对数
  63. // 然后除以Math.LN10;求以2为底的对数,可以除以Math.LN2
  64. Math.log(100)/Math.LN10 // 2
  65. Math.log(8)/Math.LN2 // 3
  66. // Math.exp方法返回常数e的参数次方
  67. Math.exp(1) // 2.718281828459045
  68. Math.exp(3) // 20.085536923187668
  69. Math.random()返回01之间的一个伪随机数,可能等于0,但是一定小于1
  70. Math.random() // 0.7151307314634323
  71. // 任意范围的随机数生成函数如下
  72. function getRandomArbitrary(min, max) {
  73. return Math.random() * (max - min) + min;
  74. }
  75. getRandomArbitrary(1.5, 6.5)
  76. // 2.4942810038223864
  77. // 任意范围的随机整数生成函数如下
  78. function getRandomInt(min, max) {
  79. return Math.floor(Math.random() * (max - min + 1)) + min;
  80. }
  81. getRandomInt(1, 6) // 5
  82. // 返回随机字符的例子如下
  83. function random_str(length) {
  84. var ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  85. ALPHABET += 'abcdefghijklmnopqrstuvwxyz';
  86. ALPHABET += '0123456789-_';
  87. var str = '';
  88. for (var i = 0; i < length; ++i) {
  89. var rand = Math.floor(Math.random() * ALPHABET.length);
  90. str += ALPHABET.substring(rand, rand + 1);
  91. }
  92. return str;
  93. }
  94. random_str(6) // "NdQKOr"

Math对象还提供一系列三角函数方法

  1. Math.sin():返回参数的正弦(参数为弧度值)
  2. Math.cos():返回参数的余弦(参数为弧度值)
  3. Math.tan():返回参数的正切(参数为弧度值)
  4. Math.asin():返回参数的反正弦(返回值为弧度值)
  5. Math.acos():返回参数的反余弦(返回值为弧度值)
  6. Math.atan():返回参数的反正切(返回值为弧度值)
  7. Math.sin(0) // 0
  8. Math.cos(0) // 1
  9. Math.tan(0) // 0
  10. Math.sin(Math.PI / 2) // 1
  11. Math.asin(1) // 1.5707963267948966
  12. Math.acos(1) // 0
  13. Math.atan(1) // 0.7853981633974483

9. Date 对象

  • Date对象是 JavaScript 原生的时间库
  • 它以国际标准时间(UTC)1970年1月1日00:00:00作为时间的零点
  • 可以表示的时间范围是前后各1亿天(单位为毫秒)

普通函数的用法

  1. // Date对象可以作为普通函数直接调用,返回一个代表当前时间的字符串
  2. Date()
  3. // "Wed Jun 17 2020 22:19:51 GMT+0800 (中国标准时间)"
  4. Date(2020, 1, 1)
  5. // "Wed Jun 17 2020 22:20:44 GMT+0800 (中国标准时间)"
  6. // 无论有没有参数,直接调用Date总是返回当前时间

构造函数的用法

  1. var today = new Date();
  2. // Date实例有一个独特的地方
  3. // 其他对象求值的时候,都是默认调用.valueOf()方法
  4. // 但是Date实例求值的时候,默认调用的是toString()方法。这导致对Date实例求值,返回的是一个字符串
  5. // 代表该实例对应的时间
  6. var today = new Date();
  7. today
  8. // "Tue Dec 01 2015 09:34:43 GMT+0800 (CST)"
  9. // 等同于
  10. today.toString()
  11. // "Tue Dec 01 2015 09:34:43 GMT+0800 (CST)"
  12. // today是Date的实例,直接求值等同于调用toString方法
  13. // 作为构造函数时,Date对象可以接受多种格式的参数,返回一个该参数对应的时间实例
  14. // 参数为时间零点开始计算的毫秒数
  15. new Date(1378218728000)
  16. // Tue Sep 03 2013 22:32:08 GMT+0800 (CST)
  17. // 参数为日期字符串
  18. new Date('January 6, 2013');
  19. // Sun Jan 06 2013 00:00:00 GMT+0800 (CST)
  20. // 参数为多个整数,
  21. // 代表年、月、日、小时、分钟、秒、毫秒
  22. new Date(2013, 0, 1, 0, 0, 0, 0)
  23. // Tue Jan 01 2013 00:00:00 GMT+0800 (CST)
  24. // 关于Date构造函数的参数,有几点说明
  25. // 第一点,参数可以是负整数,代表1970年元旦之前的时间
  26. new Date(-1378218728000)
  27. // Fri Apr 30 1926 17:27:52 GMT+0800 (CST)
  28. // 第二点,只要是能被Date.parse()方法解析的字符串,都可以当作参数
  29. new Date('2013-2-15')
  30. new Date('2013/2/15')
  31. new Date('02/15/2013')
  32. new Date('2013-FEB-15')
  33. new Date('FEB, 15, 2013')
  34. new Date('FEB 15, 2013')
  35. new Date('February, 15, 2013')
  36. new Date('February 15, 2013')
  37. new Date('15 Feb 2013')
  38. new Date('15, February, 2013')
  39. // Fri Feb 15 2013 00:00:00 GMT+0800 (CST)
  40. // 第三,参数为年、月、日等多个整数时,年和月是不能省略的
  41. // 其他参数都可以省略的,也就是说,这时至少需要两个参数
  42. // 因为如果只使用“年”这一个参数,Date会将其解释为毫秒数
  43. new Date(2013)
  44. // Thu Jan 01 1970 08:00:02 GMT+0800 (CST)
  45. new Date(2013, 0)
  46. // Tue Jan 01 2013 00:00:00 GMT+0800 (CST)
  47. new Date(2013, 0, 1)
  48. // Tue Jan 01 2013 00:00:00 GMT+0800 (CST)
  49. new Date(2013, 0, 1, 0)
  50. // Tue Jan 01 2013 00:00:00 GMT+0800 (CST)
  51. new Date(2013, 0, 1, 0, 0, 0, 0)
  52. // Tue Jan 01 2013 00:00:00 GMT+0800 (CST)
  53. // 上面代码中,不管有几个参数,返回的都是2013年1月1日零点
  54. // 各个参数的取值范围如下
  55. 年:使用四位数年份,比如2000。如果写成两位数或个位数,则加上1900,即10代表1910年,如果是负数,表示公元前。
  56. 月:0表示一月,依次类推,11表示12
  57. 日:131
  58. 小时:023
  59. 分钟:059
  60. 秒:059
  61. 毫秒:0999
  62. // 注意,月份从0开始计算,但是,天数从1开始计算
  63. // 另外,除了日期的默认值为1,小时、分钟、秒钟和毫秒的默认值都是0
  64. // 这些参数如果超出了正常范围,会被自动折算。比如,如果月设为15,就折算为下一年的4月
  65. new Date(2013, 15)
  66. // Tue Apr 01 2014 00:00:00 GMT+0800 (CST)
  67. new Date(2013, 0, 0)
  68. // Mon Dec 31 2012 00:00:00 GMT+0800 (CST)
  69. // 参数还可以使用负数,表示扣去的时间
  70. new Date(2013, -1)
  71. // Sat Dec 01 2012 00:00:00 GMT+0800 (CST)
  72. new Date(2013, 0, -1)
  73. // Sun Dec 30 2012 00:00:00 GMT+0800 (CST)
  74. // 上面代码中,分别对月和日使用了负数,表示从基准日扣去相应的时间

日期的运算

  1. // 类型自动转换时,Date实例如果转为数值,则等于对应的毫秒数
  2. // 如果转为字符串,则等于对应的日期字符串
  3. var d1 = new Date(2000, 2, 1);
  4. var d2 = new Date(2000, 3, 1);
  5. d2 - d1
  6. // 2678400000
  7. d2 + d1
  8. // "Sat Apr 01 2000 00:00:00 GMT+0800 (CST)Wed Mar 01 2000 00:00:00 GMT+0800 (CST)"

静态方法

  1. // Date.now方法返回当前时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数
  2. // 相当于 Unix 时间戳乘以1000
  3. Date.now() // 1364026285194
  4. // Date.parse方法用来解析日期字符串
  5. // 返回该时间距离时间零点(1970年1月1日 00:00:00)的毫秒数
  6. // 日期字符串应该符合 RFC 2822 和 ISO 8061 这两个标准
  7. // YYYY-MM-DDTHH:mm:ss.sssZ格式,其中最后的Z表示时区
  8. // 但是,其他格式也可以被解析
  9. Date.parse('Aug 9, 1995')
  10. Date.parse('January 26, 2011 13:51:50')
  11. Date.parse('Mon, 25 Dec 1995 13:30:00 GMT')
  12. Date.parse('Mon, 25 Dec 1995 13:30:00 +0430')
  13. Date.parse('2011-10-10')
  14. Date.parse('2011-10-10T14:48:00')
  15. // 如果解析失败,返回NaN
  16. Date.parse('xxx') // NaN
  17. // Date.UTC方法接受年、月、日等变量作为参数
  18. // 返回该时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数
  19. // 格式
  20. Date.UTC(year, month[, date[, hrs[, min[, sec[, ms]]]]])
  21. // 用法
  22. Date.UTC(2011, 0, 1, 2, 3, 4, 567)
  23. // 1293847384567

实例方法

  • Date的实例对象,有几十个自己的方法,除了valueOf和toString,可以分为以下三类
    • to类:从Date对象返回一个字符串,表示指定的时间
    • get类:获取Date对象的日期和时间
    • set类:设置Date对象的日期和时间
  1. // valueOf方法返回实例对象距离时间零点(1970年1月1日00:00:00 UTC)对应的毫秒数
  2. // 该方法等同于getTime方法
  3. var d = new Date();
  4. d.valueOf() // 1362790014817
  5. d.getTime() // 1362790014817
  6. // 预期为数值的场合,Date实例会自动调用该方法,所以可以用下面的方法计算时间的间隔
  7. ar start = new Date();
  8. // ...
  9. var end = new Date();
  10. var elapsed = end - start;

to 类方法

  1. // toString方法返回一个完整的日期字符串
  2. var d = new Date(2013, 0, 1);
  3. d.toString()
  4. // "Tue Jan 01 2013 00:00:00 GMT+0800 (CST)"
  5. d
  6. // "Tue Jan 01 2013 00:00:00 GMT+0800 (CST)"
  7. // toUTCString方法返回对应的 UTC 时间,也就是比北京时间晚8个小时
  8. var d = new Date(2013, 0, 1);
  9. d.toUTCString()
  10. // "Mon, 31 Dec 2012 16:00:00 GMT"
  11. // toISOString方法返回对应时间的 ISO8601 写法
  12. var d = new Date(2013, 0, 1);
  13. d.toISOString()
  14. // "2012-12-31T16:00:00.000Z"
  15. // toJSON方法返回一个符合 JSON 格式的 ISO 日期字符串
  16. // 与toISOString方法的返回结果完全相同
  17. var d = new Date(2013, 0, 1);
  18. d.toJSON()
  19. // "2012-12-31T16:00:00.000Z"
  20. // toDateString方法返回日期字符串(不含小时、分和秒)
  21. var d = new Date(2013, 0, 1);
  22. d.toDateString() // "Tue Jan 01 2013"
  23. // toTimeString方法返回时间字符串(不含年月日)
  24. var d = new Date(2013, 0, 1);
  25. d.toTimeString() // "00:00:00 GMT+0800 (CST)"
  26. // 本地时间
  27. var d = new Date(2013, 0, 1);
  28. d.toLocaleString()
  29. // 中文版浏览器为"2013年1月1日 上午12:00:00"
  30. // 英文版浏览器为"1/1/2013 12:00:00 AM"
  31. d.toLocaleDateString()
  32. // 中文版浏览器为"2013年1月1日"
  33. // 英文版浏览器为"1/1/2013"
  34. d.toLocaleTimeString()
  35. // 中文版浏览器为"上午12:00:00"
  36. // 英文版浏览器为"12:00:00 AM"
  37. // 这三个方法都有两个可选的参数
  38. // 这两个参数中,locales是一个指定所用语言的字符串,options是一个配置对象
  39. dateObj.toLocaleString([locales[, options]])
  40. dateObj.toLocaleDateString([locales[, options]])
  41. dateObj.toLocaleTimeString([locales[, options]])
  42. var d = new Date(2013, 0, 1);
  43. d.toLocaleString('en-US') // "1/1/2013, 12:00:00 AM"
  44. d.toLocaleString('zh-CN') // "2013/1/1 上午12:00:00"
  45. d.toLocaleDateString('en-US') // "1/1/2013"
  46. d.toLocaleDateString('zh-CN') // "2013/1/1"
  47. d.toLocaleTimeString('en-US') // "12:00:00 AM"
  48. d.toLocaleTimeString('zh-CN') // "上午12:00:00"
  49. // options配置对象有以下属性
  50. dateStyle:可能的值为fulllongmediumshort
  51. timeStyle:可能的值为fulllongmediumshort
  52. month:可能的值为numeric2-digitlongshortnarrow
  53. year:可能的值为numeric2-digit
  54. weekday:可能的值为longshortnarrow
  55. dayhourminutesecond:可能的值为numeric2-digit
  56. timeZone:可能的值为 IANA 的时区数据库。
  57. timeZooneName:可能的值为longshort
  58. hour1224小时周期还是12小时周期,可能的值为truefalse
  59. // 用法如下
  60. var d = new Date(2013, 0, 1);
  61. d.toLocaleDateString('en-US', {
  62. weekday: 'long',
  63. year: 'numeric',
  64. month: 'long',
  65. day: 'numeric'
  66. })
  67. // "Tuesday, January 1, 2013"
  68. d.toLocaleDateString('en-US', {
  69. day: "2-digit",
  70. month: "long",
  71. year: "2-digit"
  72. });
  73. // "January 01, 13"
  74. d.toLocaleTimeString('en-US', {
  75. timeZone: 'UTC',
  76. timeZoneName: 'short'
  77. })
  78. // "4:00:00 PM UTC"
  79. d.toLocaleTimeString('en-US', {
  80. timeZone: 'Asia/Shanghai',
  81. timeZoneName: 'long'
  82. })
  83. // "12:00:00 AM China Standard Time"
  84. d.toLocaleTimeString('en-US', {
  85. hour12: false
  86. })
  87. // "00:00:00"
  88. d.toLocaleTimeString('en-US', {
  89. hour12: true
  90. })
  91. // "12:00:00 AM"

get 类方法

  1. // Date对象提供了一系列get*方法,用来获取实例对象某个方面的值
  2. getTime():返回实例距离19701100:00:00的毫秒数,等同于valueOf方法。
  3. getDate():返回实例对象对应每个月的几号(从1开始)。
  4. getDay():返回星期几,星期日为0,星期一为1,以此类推。
  5. getFullYear():返回四位的年份。
  6. getMonth():返回月份(0表示1月,11表示12月)。
  7. getHours():返回小时(0-23)。
  8. getMilliseconds():返回毫秒(0-999)。
  9. getMinutes():返回分钟(0-59)。
  10. getSeconds():返回秒(0-59)。
  11. getTimezoneOffset():返回当前时间与 UTC 的时区差异,以分钟表示,返回结果考虑到了夏令时因素
  12. // 所有这些get*方法返回的都是整数,不同方法返回值的范围不一样。
  13. 分钟和秒:0 59
  14. 小时:0 23
  15. 星期:0(星期天)到 6(星期六)
  16. 日期:1 31
  17. 月份:0(一月)到 11(十二月)
  18. var d = new Date('January 6, 2013');
  19. d.getDate() // 6
  20. d.getMonth() // 0
  21. d.getFullYear() // 2013
  22. d.getTimezoneOffset() // -480
  23. 下面是一个例子,计算本年度还剩下多少天
  24. // function leftDays() {
  25. var today = new Date();
  26. var endYear = new Date(today.getFullYear(), 11, 31, 23, 59, 59, 999);
  27. var msPerDay = 24 * 60 * 60 * 1000;
  28. return Math.round((endYear.getTime() - today.getTime()) / msPerDay);
  29. }
  30. // 上面这些get*方法返回的都是当前时区的时间
  31. // Date对象还提供了这些方法对应的 UTC 版本,用来返回 UTC 时间
  32. getUTCDate()
  33. getUTCFullYear()
  34. getUTCMonth()
  35. getUTCDay()
  36. getUTCHours()
  37. getUTCMinutes()
  38. getUTCSeconds()
  39. getUTCMilliseconds()
  40. var d = new Date('January 6, 2013');
  41. d.getDate() // 6
  42. d.getUTCDate() // 5

set 类方法

  1. // Date对象提供了一系列set*方法,用来设置实例对象的各个方面
  2. setDate(date):设置实例对象对应的每个月的几号(1-31),返回改变后毫秒时间戳
  3. setFullYear(year [, month, date]):设置四位年份
  4. setHours(hour [, min, sec, ms]):设置小时(0-23
  5. setMilliseconds():设置毫秒(0-999
  6. setMinutes(min [, sec, ms]):设置分钟(0-59
  7. setMonth(month [, date]):设置月份(0-11
  8. setSeconds(sec [, ms]):设置秒(0-59
  9. setTime(milliseconds):设置毫秒时间戳
  10. // 这些方法基本是跟get*方法一一对应的,但是没有setDay方法
  11. // 因为星期几是计算出来的,而不是设置的
  12. // 另外,需要注意的是,凡是涉及到设置月份,都是从0开始算的,即0是1月,11是12月
  13. var d = new Date ('January 6, 2013');
  14. d // Sun Jan 06 2013 00:00:00 GMT+0800 (CST)
  15. d.setDate(9) // 1357660800000
  16. d // Wed Jan 09 2013 00:00:00 GMT+0800 (CST)
  17. // set*方法的参数都会自动折算
  18. // 以setDate()为例,如果参数超过当月的最大天数,则向下一个月顺延
  19. // 如果参数是负数,表示从上个月的最后一天开始减去的天数
  20. var d1 = new Date('January 6, 2013');
  21. d1.setDate(32) // 1359648000000
  22. d1 // Fri Feb 01 2013 00:00:00 GMT+0800 (CST)
  23. var d2 = new Date ('January 6, 2013');
  24. d2.setDate(-1) // 1356796800000
  25. d2 // Sun Dec 30 2012 00:00:00 GMT+0800 (CST)
  26. // set类方法和get类方法,可以结合使用,得到相对时间
  27. var d = new Date();
  28. // 将日期向后推1000天
  29. d.setDate(d.getDate() + 1000);
  30. // 将时间设为6小时后
  31. d.setHours(d.getHours() + 6);
  32. // 将年份设为去年
  33. d.setFullYear(d.getFullYear() - 1);
  34. // set*系列方法除了setTime(),都有对应的 UTC 版本,即设置 UTC 时区的时间
  35. setUTCDate()
  36. setUTCFullYear()
  37. setUTCHours()
  38. setUTCMilliseconds()
  39. setUTCMinutes()
  40. setUTCMonth()
  41. setUTCSeconds()
  42. var d = new Date('January 6, 2013');
  43. d.getUTCHours() // 16
  44. d.setUTCHours(22) // 1357423200000
  45. d // Sun Jan 06 2013 06:00:00 GMT+0800 (CST)

10. RegExp 对象

  • 正则表达式(regular expression)是一种表达文本模式(即字符串结构)的方法
  • 有点像字符串的模板,常常用来按照“给定模式”匹配文本
  • 新建正则表达式有两种方法
    • 一种是使用字面量,以斜杠表示开始和结束
    • 另一种是使用RegExp构造函数
  1. var regex = /xyz/;
  2. var regex = new RegExp('xyz');
  3. // 前者的效率较高
  4. // 而且,前者比较便利和直观,所以实际应用中,基本上都采用字面量定义正则表达式
  5. // RegExp构造函数还可以接受第二个参数,表示修饰符(详细解释见下文)
  6. var regex = new RegExp('xyz', 'i');
  7. // 等价于
  8. var regex = /xyz/i;

实例属性

  • 正则对象的实例属性分成两类
    • 一类是修饰符相关,用于了解设置了什么修饰符
    • 另一类是与修饰符无关的属性
  1. RegExp.prototype.ignoreCase:返回一个布尔值,表示是否设置了i修饰符
  2. RegExp.prototype.global:返回一个布尔值,表示是否设置了g修饰符
  3. RegExp.prototype.multiline:返回一个布尔值,表示是否设置了m修饰符
  4. RegExp.prototype.flags:返回一个字符串,包含了已经设置的所有修饰符,按字母排序
  5. // 上面四个属性都是只读的
  6. var r = /abc/igm;
  7. r.ignoreCase // true
  8. r.global // true
  9. r.multiline // true
  10. r.flags // 'gim'
  11. RegExp.prototype.lastIndex:返回一个整数,表示下一次开始搜索的位置
  12. // 该属性可读写,但是只在进行连续搜索时有意义
  13. RegExp.prototype.source:返回正则表达式的字符串形式(不包括反斜杠),该属性只读
  14. var r = /abc/igm;
  15. r.lastIndex // 0
  16. r.source // "abc"

正则实例对象的test方法返回一个布尔值

  • 表示当前模式是否能匹配参数字符串
  1. /cat/.test('cats and dogs') // true
  2. // 如果正则表达式带有g修饰符,则每一次test方法都从上一次结束的位置开始向后匹配
  3. var r = /x/g;
  4. var s = '_x_x';
  5. r.lastIndex // 0
  6. r.test(s) // true
  7. r.lastIndex // 2
  8. r.test(s) // true
  9. r.lastIndex // 4
  10. r.test(s) // false
  11. // 上面代码的正则表达式使用了g修饰符,表示是全局搜索,会有多个结果
  12. // 接着,三次使用test方法,每一次开始搜索的位置都是上一次匹配的后一个位置
  13. // 带有g修饰符时,可以通过正则对象的lastIndex属性指定开始搜索的位置
  14. var r = /x/g;
  15. var s = '_x_x';
  16. r.lastIndex = 4;
  17. r.test(s) // false
  18. r.lastIndex // 0
  19. r.test(s)
  20. // 注意,带有g修饰符时,正则表达式内部会记住上一次的lastIndex属性
  21. // 这时不应该更换所要匹配的字符串,否则会有一些难以察觉的错误
  22. var r = /bb/g;
  23. r.test('bb') // true
  24. r.test('-bb-') // false
  25. // 上面代码中,由于正则表达式r是从上一次的lastIndex位置开始匹配
  26. // 导致第二次执行test方法时出现预期以外的结果
  27. // 如果正则模式是一个空字符串,则匹配所有字符串
  28. new RegExp('').test('abc')
  29. // true

正则实例对象的exec()方法,用来返回匹配结果

  • 如果发现匹配,就返回一个数组,成员是匹配成功的子字符串
  • 否则返回null
  1. var s = '_x_x';
  2. var r1 = /x/;
  3. var r2 = /y/;
  4. r1.exec(s) // ["x"]
  5. r2.exec(s) // null
  6. // 组匹配
  7. var s = '_x_x';
  8. var r = /_(x)/;
  9. r.exec(s) // ["_x", "x"]
  10. // exec()方法的返回数组还包含以下两个属性:
  11. input:整个原字符串
  12. index:模式匹配成功的开始位置(从0开始计数)
  13. var r = /a(b+)a/;
  14. var arr = r.exec('_abbba_aba_');
  15. arr // ["abbba", "bbb"]
  16. arr.index // 1
  17. arr.input // "_abbba_aba_"
  18. // 上面代码中的index属性等于1,是因为从原字符串的第二个位置开始匹配成功。
  19. // 如果正则表达式加上g修饰符,则可以使用多次exec()方法
  20. // 下一次搜索的位置从上一次匹配成功结束的位置开始
  21. var reg = /a/g;
  22. var str = 'abc_abc_abc'
  23. var r1 = reg.exec(str);
  24. r1 // ["a"]
  25. r1.index // 0
  26. reg.lastIndex // 1
  27. var r2 = reg.exec(str);
  28. r2 // ["a"]
  29. r2.index // 4
  30. reg.lastIndex // 5
  31. var r3 = reg.exec(str);
  32. r3 // ["a"]
  33. r3.index // 8
  34. reg.lastIndex // 9
  35. var r4 = reg.exec(str);
  36. r4 // null
  37. reg.lastIndex // 0
  38. // 利用g修饰符允许多次匹配的特点,可以用一个循环完成全部匹配
  39. var reg = /a/g;
  40. var str = 'abc_abc_abc'
  41. while(true) {
  42. var match = reg.exec(str);
  43. if (!match) break;
  44. console.log('#' + match.index + ':' + match[0]);
  45. }
  46. // #0:a
  47. // #4:a
  48. // #8:a

字符串的实例方法

  • String.prototype.match():返回一个数组,成员是所有匹配的子字符串
  • String.prototype.search():按照给定的正则表达式进行搜索,返回一个整数,表示匹配开始的位置
  • String.prototype.replace():按照给定的正则表达式进行替换,返回替换后的字符串
  • String.prototype.split():按照给定规则进行字符串分割,返回一个数组,包含分割后的各个成员

字符串实例对象的match方法对字符串进行正则匹配,返回匹配结果

  1. var s = '_x_x';
  2. var r1 = /x/;
  3. var r2 = /y/;
  4. s.match(r1) // ["x"]
  5. s.match(r2) // null
  6. // 如果正则表达式带有g修饰符,则该方法与正则对象的exec方法行为不同
  7. // 会一次性返回所有匹配成功的结果
  8. var s = 'abba';
  9. var r = /a/g;
  10. s.match(r) // ["a", "a"]
  11. r.exec(s) // ["a"]
  12. // 设置正则表达式的lastIndex属性,对match方法无效
  13. // 匹配总是从字符串的第一个字符开始
  14. var r = /a|b/g;
  15. r.lastIndex = 7;
  16. 'xaxb'.match(r) // ['a', 'b']
  17. r.lastIndex // 0

字符串对象的search方法,返回第一个满足条件的匹配结果在整个字符串中的位置

  • 如果没有任何匹配,则返回-1
  1. '_x_x'.search(/x/)
  2. // 1

字符串对象的replace方法可以替换匹配的值,它接受两个参数

  • 第一个是正则表达式,表示搜索模式
  • 第二个是替换的内容
  1. str.replace(search, replacement)
  2. // 正则表达式如果不加g修饰符,就替换第一个匹配成功的值,否则替换所有匹配成功的值
  3. 'aaa'.replace('a', 'b') // "baa"
  4. 'aaa'.replace(/a/, 'b') // "baa"
  5. 'aaa'.replace(/a/g, 'b') // "bbb"
  6. // replace方法的一个应用,就是消除字符串首尾两端的空格
  7. var str = ' #id div.class ';
  8. str.replace(/^\s+|\s+$/g, '')
  9. // "#id div.class"
  10. // replace方法的第二个参数可以使用美元符号$,用来指代所替换的内容
  11. $&:匹配的子字符串。
  12. $`:匹配结果前面的文本
  13. $':匹配结果后面的文本
  14. $n:匹配成功的第n组内容,n是从1开始的自然数
  15. $$:指代美元符号$
  16. 'hello world'.replace(/(\w+)\s(\w+)/, '$2 $1')
  17. // "world hello"
  18. 'abc'.replace('b', '[$`-$&-$\']')
  19. // "a[a-b-c]c"
  20. // replace方法的第二个参数还可以是一个函数,将每一个匹配内容替换为函数返回值
  21. '3 and 5'.replace(/[0-9]+/g, function (match) {
  22. return 2 * match;
  23. })
  24. // "6 and 10"
  25. var a = 'The quick brown fox jumped over the lazy dog.';
  26. var pattern = /quick|brown|lazy/ig;
  27. a.replace(pattern, function replacer(match) {
  28. return match.toUpperCase();
  29. });
  30. // The QUICK BROWN fox jumped over the LAZY dog.
  31. // 作为replace方法第二个参数的替换函数,可以接受多个参数
  32. // 第一个参数是捕捉到的内容
  33. // 第二个参数是捕捉到的组匹配(有多少个组匹配,就有多少个对应的参数)
  34. // 此外,最后还可以添加两个参数
  35. // 倒数第二个参数是捕捉到的内容在整个字符串中的位置(比如从第五个位置开始)
  36. // 最后一个参数是原字符串
  37. var prices = {
  38. 'p1': '$1.99',
  39. 'p2': '$9.99',
  40. 'p3': '$5.00'
  41. };
  42. var template = '<span id="p1"></span>'
  43. + '<span id="p2"></span>'
  44. + '<span id="p3"></span>';
  45. template.replace(
  46. /(<span id=")(.*?)(">)(<\/span>)/g,
  47. function(match, $1, $2, $3, $4){
  48. return $1 + $2 + $3 + prices[$2] + $4;
  49. }
  50. );
  51. // "<span id="p1">$1.99</span><span id="p2">$9.99</span><span id="p3">$5.00</span>"

字符串对象的split方法按照正则规则分割字符串,返回一个由分割后的各个部分组成的数组

  1. str.split(separator, [limit])
  2. // 该方法接受两个参数,第一个参数是正则表达式,表示分隔规则,第二个参数是返回数组的最大成员数
  3. // 非正则分隔
  4. 'a, b,c, d'.split(',')
  5. // [ 'a', ' b', 'c', ' d' ]
  6. // 正则分隔,去除多余的空格
  7. 'a, b,c, d'.split(/, */)
  8. // [ 'a', 'b', 'c', 'd' ]
  9. // 指定返回数组的最大成员
  10. 'a, b,c, d'.split(/, */, 2)
  11. [ 'a', 'b' ]
  12. // 例一
  13. 'aaa*a*'.split(/a*/)
  14. // [ '', '*', '*' ]
  15. // 例二
  16. 'aaa**a*'.split(/a*/)
  17. // ["", "*", "*", "*"]
  18. // 上面代码的分割规则是0次或多次的a,由于正则默认是贪婪匹配
  19. // 所以例一的第一个分隔符是aaa,第二个分割符是a
  20. // 将字符串分成三个部分,包含开始处的空字符串
  21. // 例二的第一个分隔符是aaa,第二个分隔符是0个a(即空字符)
  22. // 第三个分隔符是a,所以将字符串分成四个部分
  23. // 如果正则表达式带有括号,则括号匹配的部分也会作为数组成员返回
  24. 'aaa*a*'.split(/(a*)/)
  25. // [ '', 'aaa', '*', 'a', '*' ]

正则表达式的规则:https://wangdoc.com/javascript/stdlib/regexp.html

  • 看阮一峰老师写的吧,后面我没看完,下次再来看

字面量字符和元字符

  1. // 字面量字符
  2. /dog/.test('old dog') // true
  3. // 点字符
  4. /c.t/
  5. // 位置字符
  6. // test必须出现在开始位置
  7. /^test/.test('test123') // true
  8. // test必须出现在结束位置
  9. /test$/.test('new test') // true
  10. // 从开始位置到结束位置只有test
  11. /^test$/.test('test') // true
  12. /^test$/.test('test test') // false
  13. // 选择符(|)
  14. /11|22/.test('911') // true
  15. // 匹配fred、barney、betty之中的一个
  16. /fred|barney|betty/
  17. /a( |\t)b/.test('a\tb') // true

转义符

  1. /1+1/.test('1+1')
  2. // false
  3. /1\+1/.test('1+1')
  4. // true
  5. // 正则表达式中,需要反斜杠转义的,一共有12个字符
  6. ^ . [ $ ( ) | * + ? { \
  7. (new RegExp('1\+1')).test('1+1')
  8. // false
  9. (new RegExp('1\\+1')).test('1+1')
  10. // true

特殊字符

  1. \cX 表示Ctrl-[X],其中的XA-Z之中任一个英文字母,用来匹配控制字符。
  2. [\b] 匹配退格键(U+0008),不要与\b混淆。
  3. \n 匹配换行键。
  4. \r 匹配回车键。
  5. \t 匹配制表符 tabU+0009)。
  6. \v 匹配垂直制表符(U+000B)。
  7. \f 匹配换页符(U+000C)。
  8. \0 匹配null字符(U+0000)。
  9. \xhh 匹配一个以两位十六进制数(\x00-\xFF)表示的字符。
  10. \uhhhh 匹配一个以四位十六进制数(\u0000-\uFFFF)表示的 Unicode 字符。

字符类

  1. /[abc]/.test('hello world') // false
  2. /[abc]/.test('apple') // true
  3. // 脱字符(^)
  4. /[^abc]/.test('bbc news') // true
  5. /[^abc]/.test('bbc') // false
  6. var s = 'Please yes\nmake my day!';
  7. s.match(/yes.*day/) // null
  8. s.match(/yes[^]*day/) // [ 'yes\nmake my day']
  9. // 连字符(-)
  10. /a-z/.test('b') // false
  11. /[a-z]/.test('b') // true
  12. [0-9.,]
  13. [0-9a-fA-F]
  14. [a-zA-Z0-9-]
  15. [1-31]
  16. var str = "\u0130\u0131\u0132";
  17. /[\u0128-\uFFFF]/.test(str)
  18. // true
  19. /[A-z]/.test('\\') // true

预定义模式

  1. \d 匹配0-9之间的任一数字,相当于[0-9]。
  2. \D 匹配所有0-9以外的字符,相当于[^0-9]。
  3. \w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]。
  4. \W 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]。
  5. \s 匹配空格(包括换行符、制表符、空格符等),相等于[ \t\r\n\v\f]。
  6. \S 匹配非空格的字符,相当于[^ \t\r\n\v\f]。
  7. \b 匹配词的边界。
  8. \B 匹配非词边界,即在词的内部
  9. // 一些例子
  10. // \s 的例子
  11. /\s\w*/.exec('hello world') // [" world"]
  12. // \b 的例子
  13. /\bworld/.test('hello world') // true
  14. /\bworld/.test('hello-world') // true
  15. /\bworld/.test('helloworld') // false
  16. // \B 的例子
  17. /\Bworld/.test('hello-world') // false
  18. /\Bworld/.test('helloworld') // true
  19. // 通常,正则表达式遇到换行符(\n)就会停止匹配
  20. var html = "<b>Hello</b>\n<i>world!</i>";
  21. /.*/.exec(html)[0]
  22. // "<b>Hello</b>"
  23. var html = "<b>Hello</b>\n<i>world!</i>";
  24. /[\S\s]*/.exec(html)[0]
  25. // "<b>Hello</b>\n<i>world!</i>"

重复类

  1. /lo{2}k/.test('look') // true
  2. /lo{2,5}k/.test('looook') // true

量词符

  1. // t 出现0次或1次
  2. /t?est/.test('test') // true
  3. /t?est/.test('est') // true
  4. // t 出现1次或多次
  5. /t+est/.test('test') // true
  6. /t+est/.test('ttest') // true
  7. /t+est/.test('est') // false
  8. // t 出现0次或多次
  9. /t*est/.test('test') // true
  10. /t*est/.test('ttest') // true
  11. /t*est/.test('tttest') // true
  12. /t*est/.test('est') // true

贪婪模式和非贪婪模式

  1. // 贪婪模式
  2. var s = 'aaa';
  3. s.match(/a+/) // ["aaa"]
  4. // 非贪婪模式
  5. var s = 'aaa';
  6. s.match(/a+?/) // ["a"]
  7. 'abb'.match(/ab*/) // ["abb"]
  8. 'abb'.match(/ab*?/) // ["a"]
  9. 'abb'.match(/ab?/) // ["ab"]
  10. 'abb'.match(/ab??/) // ["a"]

修饰符

  1. // 单个修饰符
  2. var regex = /test/i;
  3. // 多个修饰符
  4. var regex = /test/ig;
  5. // g 修饰符
  6. var regex = /b/;
  7. var str = 'abba';
  8. regex.test(str); // true
  9. regex.test(str); // true
  10. regex.test(str); // true
  11. var regex = /b/g;
  12. var str = 'abba';
  13. regex.test(str); // true
  14. regex.test(str); // true
  15. regex.test(str); // false
  16. // i 修饰符
  17. /abc/.test('ABC') // false
  18. /abc/i.test('ABC') // true
  19. // m 修饰符
  20. /world$/.test('hello world\n') // false
  21. /world$/m.test('hello world\n') // true
  22. /^b/m.test('a\nb') // true

11. JSON 对象

JSON 格式

  1. // 合法的 JSON 对象
  2. ["one", "two", "three"]
  3. { "one": 1, "two": 2, "three": 3 }
  4. {"names": ["张三", "李四"] }
  5. [ { "name": "张三"}, {"name": "李四"} ]
  6. // 不合法的 JSON
  7. { name: "张三", 'age': 32 } // 属性名必须使用双引号
  8. [32, 64, 128, 0xFFF] // 不能使用十六进制值
  9. { "name": "张三", "age": undefined } // 不能使用 undefined
  10. { "name": "张三",
  11. "birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
  12. "getName": function () {
  13. return this.name;
  14. }
  15. } // 属性值不能使用函数和日期对象

JSON.stringify()

  1. JSON.stringify('abc') // ""abc""
  2. JSON.stringify(1) // "1"
  3. JSON.stringify(false) // "false"
  4. JSON.stringify([]) // "[]"
  5. JSON.stringify({}) // "{}"
  6. JSON.stringify([1, "false", false])
  7. // '[1,"false",false]'
  8. JSON.stringify({ name: "张三" })
  9. // '{"name":"张三"}'
  10. // 注意,对于原始类型的字符串,转换结果会带双引号
  11. JSON.stringify('foo') === "foo" // false
  12. JSON.stringify('foo') === "\"foo\"" // true
  13. // 如果不是内层的双引号,将来还原的时候,引擎就无法知道原始值是布尔值还是字符串
  14. JSON.stringify(false) // "false"
  15. JSON.stringify('false') // "\"false\""
  16. // 如果对象的属性是undefined、函数或 XML 对象,该属性会被JSON.stringify过滤
  17. var obj = {
  18. a: undefined,
  19. b: function () {}
  20. };
  21. JSON.stringify(obj) // "{}"
  22. // 如果数组的成员是undefined、函数或 XML 对象,则这些值被转成null
  23. var arr = [undefined, function () {}];
  24. JSON.stringify(arr) // "[null,null]"
  25. // 正则对象会被转成空对象
  26. JSON.stringify(/foo/) // "{}"
  27. JSON.stringify方法会忽略对象的不可遍历的属性
  28. var obj = {};
  29. Object.defineProperties(obj, {
  30. 'foo': {
  31. value: 1,
  32. enumerable: true
  33. },
  34. 'bar': {
  35. value: 2,
  36. enumerable: false
  37. }
  38. });
  39. JSON.stringify(obj); // "{"foo":1}"

JSON.stringify方法还可以接受一个数组,作为第二个参数,指定需要转成字符串的属性

  1. var obj = {
  2. 'prop1': 'value1',
  3. 'prop2': 'value2',
  4. 'prop3': 'value3'
  5. };
  6. var selectedProperties = ['prop1', 'prop2'];
  7. JSON.stringify(obj, selectedProperties)
  8. // "{"prop1":"value1","prop2":"value2"}"
  9. // 这个类似白名单的数组,只对对象的属性有效,对数组无效
  10. JSON.stringify(['a', 'b'], ['0'])
  11. // "["a","b"]"
  12. JSON.stringify({0: 'a', 1: 'b'}, ['0'])
  13. // "{"0":"a"}"
  14. // 第二个参数还可以是一个函数,用来更改JSON.stringify的返回值
  15. function f(key, value) {
  16. if (typeof value === "number") {
  17. value = 2 * value;
  18. }
  19. return value;
  20. }
  21. JSON.stringify({ a: 1, b: 2 }, f)
  22. // '{"a": 2,"b": 4}'
  23. // 注意,这个处理函数是递归处理所有的键
  24. var o = {a: {b: 1}};
  25. function f(key, value) {
  26. console.log("["+ key +"]:" + value);
  27. return value;
  28. }
  29. JSON.stringify(o, f)
  30. // []:[object Object]
  31. // [a]:[object Object]
  32. // [b]:1
  33. // '{"a":{"b":1}}'
  34. // 递归处理中,每一次处理的对象,都是前一次返回的值
  35. var o = {a: 1};
  36. function f(key, value) {
  37. if (typeof value === 'object') {
  38. return {b: 2};
  39. }
  40. return value * 2;
  41. }
  42. JSON.stringify(o, f)
  43. // "{"b": 4}"
  44. // 如果处理函数返回undefined或没有返回值,则该属性会被忽略
  45. function f(key, value) {
  46. if (typeof(value) === "string") {
  47. return undefined;
  48. }
  49. return value;
  50. }
  51. JSON.stringify({ a: "abc", b: 123 }, f)
  52. // '{"b": 123}'

JSON.stringify还可以接受第三个参数,用于增加返回的 JSON 字符串的可读性。如果是数字,表示每个属性前面添加的空格(最多不超过10个);如果是字符串(不超过10个字符),则该字符串会添加在每行前面

  1. JSON.stringify({ p1: 1, p2: 2 }, null, 2);
  2. /*
  3. "{
  4. "p1": 1,
  5. "p2": 2
  6. }"
  7. */
  8. JSON.stringify({ p1:1, p2:2 }, null, '|-');
  9. /*
  10. "{
  11. |-"p1": 1,
  12. |-"p2": 2
  13. }"
  14. */
  15. // 参数对象的 toJSON 方法
  16. // 如果参数对象有自定义的toJSON方法
  17. // 那么JSON.stringify会使用这个方法的返回值作为参数
  18. // 而忽略原对象的其他属性

JSON.parse()

  1. JSON.parse('{}') // {}
  2. JSON.parse('true') // true
  3. JSON.parse('"foo"') // "foo"
  4. JSON.parse('[1, 5, "false"]') // [1, 5, "false"]
  5. JSON.parse('null') // null
  6. var o = JSON.parse('{"name": "张三"}');
  7. o.name // 张三
  8. // 如果传入的字符串不是有效的 JSON 格式,JSON.parse方法将报错
  9. JSON.parse("'String'") // illegal single quotes
  10. // SyntaxError: Unexpected token ILLEGAL
  11. // 为了处理解析错误,可以将JSON.parse方法放在try...catch代码块中
  12. try {
  13. JSON.parse("'String'");
  14. } catch(e) {
  15. console.log('parsing error');
  16. }
  17. // JSON.parse方法可以接受一个处理函数,作为第二个参数,用法与JSON.stringify方法类似
  18. function f(key, value) {
  19. if (key === 'a') {
  20. return value + 10;
  21. }
  22. return value;
  23. }
  24. JSON.parse('{"a": 1, "b": 2}', f)
  25. // {a: 11, b: 2}

二、面向对象编程

1. 实例对象与 new 命令

  1. var Vehicle = function (p) {
  2. this.price = p;
  3. };
  4. var v = new Vehicle(500);
  5. // 为了与普通函数区别,构造函数名字的第一个字母通常大写

构造函数的特点有两个。

  • 函数体内部使用了this关键字,代表了所要生成的对象实例。
  • 生成对象的时候,必须使用new命令。

使用new命令时,它后面的函数依次执行下面的步骤。

  1. 创建一个空对象,作为将要返回的对象实例。
  2. 将这个空对象的原型,指向构造函数的prototype属性。
  3. 将这个空对象赋值给函数内部的this关键字。
  4. 开始执行构造函数内部的代码。
  1. // 如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象
  2. // 否则,就会不管return语句,返回this对象
  3. var Vehicle = function () {
  4. this.price = 1000;
  5. return 1000;
  6. };
  7. (new Vehicle()) === 1000
  8. // false
  9. var Vehicle = function (){
  10. this.price = 1000;
  11. return { price: 2000 };
  12. };
  13. (new Vehicle()).price
  14. // 2000

new.target

  1. function f() {
  2. console.log(new.target === f);
  3. }
  4. f() // false
  5. new f() // true
  6. // 使用这个属性,可以判断函数调用的时候,是否使用new命令。
  7. function f() {
  8. if (!new.target) {
  9. throw new Error('请使用 new 命令调用!');
  10. }
  11. // ...
  12. }
  13. f() // Uncaught Error: 请使用 new 命令调用!

Object.create() 创建实例对象

  1. // 以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()方法
  2. var person1 = {
  3. name: '张三',
  4. age: 38,
  5. greeting: function() {
  6. console.log('Hi! I\'m ' + this.name + '.');
  7. }
  8. };
  9. var person2 = Object.create(person1);
  10. person2.name // 张三
  11. person2.greeting() // Hi! I'm 张三.

2. this 关键字

它总是返回一个对象

  • 简单说,this就是属性或方法“当前”所在的对象
  1. var person = {
  2. name: '张三',
  3. describe: function () {
  4. return '姓名:'+ this.name;
  5. }
  6. };
  7. person.describe()
  8. // "姓名:张三"
  9. // 网页编程的例子
  10. <input type="text" name="age" size=3 onChange="validate(this, 18, 99);">
  11. <script>
  12. function validate(obj, lowval, hival){
  13. if ((obj.value < lowval) || (obj.value > hival))
  14. console.log('Invalid Value!');
  15. }
  16. </script>
  17. // 上面代码是一个文本输入框,每当用户输入一个值,就会调用onChange回调函数,验证这个值是否在指定范围。
  18. // 浏览器会向回调函数传入当前对象,因此this就代表传入当前对象(即文本框)
  19. // 然后就可以从this.value上面读到用户的输入值

全局环境使用this,它指的就是顶层对象window。

  1. this === window // true
  2. function f() {
  3. console.log(this === window);
  4. }
  5. f() // true

构造函数中的this,指的是实例对象。

  1. var Obj = function (p) {
  2. this.p = p;
  3. };

如果对象的方法里面包含this,this的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。

  1. var obj ={
  2. foo: function () {
  3. console.log(this);
  4. }
  5. };
  6. obj.foo() // obj

但是,下面这几种用法,都会改变this的指向。

  1. // 情况一
  2. (obj.foo = obj.foo)() // window
  3. // 情况二
  4. (false || obj.foo)() // window
  5. // 情况三
  6. (1, obj.foo)() // window

如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。

  1. var a = {
  2. p: 'Hello',
  3. b: {
  4. m: function() {
  5. console.log(this.p);
  6. }
  7. }
  8. };
  9. a.b.m() // undefined
  10. var a = {
  11. b: {
  12. m: function() {
  13. console.log(this.p);
  14. },
  15. p: 'Hello'
  16. }
  17. };
  18. var hello = a.b.m;
  19. hello() // undefined

使用注意点

  1. // 避免多层 this
  2. // 由于this的指向是不确定的,所以切勿在函数中包含多层的this。
  3. var o = {
  4. f1: function () {
  5. console.log(this);
  6. var f2 = function () {
  7. console.log(this);
  8. }();
  9. }
  10. }
  11. o.f1()
  12. // Object
  13. // Window
  14. // 避免数组处理方法中的 this
  15. // 数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。
  16. var o = {
  17. v: 'hello',
  18. p: [ 'a1', 'a2' ],
  19. f: function f() {
  20. this.p.forEach(function (item) {
  21. console.log(this.v + ' ' + item);
  22. });
  23. }
  24. }
  25. o.f()
  26. // undefined a1
  27. // undefined a2
  28. // 避免回调函数中的 this
  29. // 回调函数中的this往往会改变指向,最好避免使用。
  30. var o = new Object();
  31. o.f = function () {
  32. console.log(this === o);
  33. }
  34. // jQuery 的写法
  35. $('#button').on('click', o.f);
  36. // 上面代码中,点击按钮以后,控制台会显示false。原因是此时this不再指向o对象
  37. // 而是指向按钮的 DOM 对象,因为f方法是在按钮对象的环境中被调用的。
  38. // 这种细微的差别,很容易在编程中忽视,导致难以察觉的错误。

绑定 this 的方法

  • this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。
  • 有时,需要把this固定下来,避免出现意想不到的情况。JavaScript 提供了call、apply、bind这三个方法
  • 来切换/固定this的指向。
  1. // Function.prototype.call()
  2. var obj = {};
  3. var f = function () {
  4. return this;
  5. };
  6. f() === window // true
  7. f.call(obj) === obj // true
  8. // call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象。
  9. var n = 123;
  10. var obj = { n: 456 };
  11. function a() {
  12. console.log(this.n);
  13. }
  14. a.call() // 123
  15. a.call(null) // 123
  16. a.call(undefined) // 123
  17. a.call(window) // 123
  18. a.call(obj) // 456
  19. // 如果call方法的参数是一个原始值,那么这个原始值会自动转成对应的包装对象,然后传入call方法。
  20. var f = function () {
  21. return this;
  22. };
  23. f.call(5)
  24. // Number {[[PrimitiveValue]]: 5}
  25. // call方法的一个应用是调用对象的原生方法。
  26. var obj = {};
  27. obj.hasOwnProperty('toString') // false
  28. // 覆盖掉继承的 hasOwnProperty 方法
  29. obj.hasOwnProperty = function () {
  30. return true;
  31. };
  32. obj.hasOwnProperty('toString') // true
  33. Object.prototype.hasOwnProperty.call(obj, 'toString') // false
  34. // Function.prototype.apply()
  35. // apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。
  36. // 唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。
  37. // func.apply(thisValue, [arg1, arg2, ...])
  38. // 找出数组最大元素
  39. var a = [10, 2, 4, 15, 9];
  40. Math.max.apply(null, a) // 15
  41. // 将数组的空元素变为undefined
  42. Array.apply(null, ['a', ,'b'])
  43. // [ 'a', undefined, 'b' ]
  44. // 转换类似数组的对象
  45. Array.prototype.slice.apply({0: 1, length: 1}) // [1]
  46. Array.prototype.slice.apply({0: 1}) // []
  47. Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
  48. Array.prototype.slice.apply({length: 1}) // [undefined]
  49. // 绑定回调函数的对象
  50. var o = new Object();
  51. o.f = function () {
  52. console.log(this === o);
  53. }
  54. var f = function (){
  55. o.f.apply(o);
  56. // 或者 o.f.call(o);
  57. };
  58. // jQuery 的写法
  59. $('#button').on('click', f);
  60. // Function.prototype.bind()
  61. // bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。
  62. var d = new Date();
  63. d.getTime() // 1481869925657
  64. var print = d.getTime;
  65. print() // Uncaught TypeError: this is not a Date object.
  66. var print = d.getTime.bind(d);
  67. print() // 1481869925657
  68. // bind方法的参数就是所要绑定this的对象,下面是一个更清晰的例子。
  69. var counter = {
  70. count: 0,
  71. inc: function () {
  72. this.count++;
  73. }
  74. };
  75. var func = counter.inc.bind(counter);
  76. func();
  77. counter.count // 1
  78. // 如果bind()方法的第一个参数是null或undefined,等于将this绑定到全局对象
  79. // 函数运行时this指向顶层对象(浏览器为window)。
  80. function add(x, y) {
  81. return x + y;
  82. }
  83. var plus5 = add.bind(null, 5);
  84. plus5(10) // 15

bind() 方法注意点

  1. // bind()方法每运行一次,就返回一个新函数,这会产生一些问题。
  2. // 比如,监听事件的时候,不能写成下面这样。
  3. element.addEventListener('click', o.m.bind(o));
  4. // 上面代码中,click事件绑定bind()方法生成的一个匿名函数。这样会导致无法取消绑定
  5. // 所以下面的代码是无效的。
  6. element.removeEventListener('click', o.m.bind(o));
  7. // 正确的方法是写成下面这样:
  8. var listener = o.m.bind(o);
  9. element.addEventListener('click', listener);
  10. // ...
  11. element.removeEventListener('click', listener);
  12. // 结合回调函数使用
  13. var counter = {
  14. count: 0,
  15. inc: function () {
  16. 'use strict';
  17. this.count++;
  18. }
  19. };
  20. function callIt(callback) {
  21. callback();
  22. }
  23. callIt(counter.inc.bind(counter));
  24. counter.count // 1
  25. // 结合call()方法使用
  26. [1, 2, 3].slice(0, 1) // [1]
  27. // 等同于
  28. Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]
  29. // 用bind()方法改写
  30. var slice = Function.prototype.call.bind(Array.prototype.slice);
  31. slice([1, 2, 3], 0, 1) // [1]

三、章节链接

「@浪里淘沙的小法师」