JavaScript学习笔记(基础)

225 阅读1小时+

JavaScript

一、JS编程风格是函数式编程和面向对象编程的混合体

二、基本概念

js变量是松散的动态类型,每个变量仅仅是一个保存值的占位符,所以未初始化就是一个undefined,且可以随时切换类型

在变量声明时会被分为两步:变量的声明和赋值,变量声明命令通知解释引擎,要创建一个变量

  • var a=1; ==> var a; a=1;

如果只是声明变量而没有赋值,则该变量的值是undefined,访问一个未声明的变量会导致抛出一个引用错误(ReferenceError)异常

可以使用 undefined 来判断一个变量是否已赋值(a===undefined)

变量提升hoisting

  • JS引擎工作方式是先解析代码获取所有被声明的变量语句,再按行运行,所以所有变量声明语句都被提升到代码头部

    • console.log(a); var a=1; => undefine

null表示一个空对象指针,所以typeof null == object

转为false的值:空字符串、0、NaN、null(与之相对的是任何对象)、undefined

  • Boolean({}) == true

相等和不相等:转换后再比较

全等和不全等:不转换就比较

对于var而言,区块{}不构成作用域

三、变量

声明

  • var

    • 没有块作用域、声明提升
  • let

    • 所声明的变量,只在let命令所在的代码块内有效
    • 不存在变量提升(let、const)
    • 暂时性死区 temporal dead zone 简称 TDZ
      • 在代码块内,使用let命令声明变量之前,该变量都是不可用的

      • 1)TDZ意味着typeof不再是一个百分之百安全的操作(此前即使变量不存在,也是返回undefined)

        • typeof x; // ReferenceError let x;
      • 2)函数参数也是声明

        • function bar(x = y, y = 2) { return [x, y]; }

bar(); // 报错

	- 3)声明前使用(赋值时先计算等号右边,再将计算结果赋值给左边变量)

		- // 报错

let x = x; // ReferenceError: x is not defined

	- 本质:只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量

- let不允许在相同作用域内,重复声明同一个变量
- let实际上为 JavaScript 新增了块级作用域

	- ES5 只有全局作用域和函数作用域,没有块级作用域。ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明
	- ES6 明确允许在块级作用域之中声明函数,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用
	- 块级作用域的出现,实际上使得获得广泛应用的匿名立即执行函数表达式(匿名 IIFE)不再必要
	- 但是,浏览器并不一定遵守约定,导致:

1)允许在块级作用域内声明函数。 2)函数声明类似于var,即会提升到全局作用域或函数作用域的头部。 3)函数声明还会提升到所在的块级作用域的头部。 - 考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句

- for循环的计数器,就很合适使用let命令。设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
  • const

    • const声明一个只读的常量。一旦声明,常量的值就不能改变,所以,const一旦声明变量,就必须立即初始化

    • const命令声明的常量不提升 同样存在暂时性死区 只能在声明的位置后面使用 不可重复声明 只在声明的块中有效

    • 本质:常量标识符指向的那个内存地址所保存的数据不得改动

      • 原始类型的值就存放在该块内存区域,等同于常量
      • 引用类型:标识符存放的是内存地址,即指向实际数据的指针,这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了
  • (ES6)如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错

  • ES6 一共有 6 种声明变量的方法:var、let、const、function、import、class

顶层对象

  • 在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的

  • 顶层对象的属性与全局变量挂钩,被认为是 JavaScript 语言最大的设计败笔之一

  • ES6 做出改变,规定

    • 1)为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性
    • 2)let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性
    • 也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩
  • 各种实现里面是不统一的

    • 1)浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window。 2)浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self。 3)Node 里面,顶层对象是global,但其他环境都不支持。
  • ES2020 在语言标准的层面,引入globalThis作为顶层对象。也就是说,任何环境下,globalThis都是存在的,都可以从它拿到顶层对象,指向全局环境下的this

解构赋值

  • ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)

    • let [a, b, c] = [1, 2, 3];
  • 数组

    • 本质上,属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

    • 解构不成功,变量的值就等于undefined

    • 不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组

      • let [x, y] = [1, 2, 3]; x // 1 y // 2
    • 如果等号右边是不可遍历的结构,就会报错

    • 事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值

    • 解构赋值允许指定默认值

      • let [x, y = 'b'] = ['a']; // x='a', y='b' let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
      • ES6 内部使用严格相等运算符(===),判断一个位置是否有值。只有当一个数组成员严格等于undefined,默认值才会生效,null不全等undefined
      • 如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值,所以能取到值时根本不会执行函数
      • 默认值可以引用解构赋值的其他变量,但该变量必须已经声明(TDZ)
  • 对象

    • 对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值

      • let { bar, foo } = { foo: 'aaa', bar: 'bbb' }; foo // "aaa" bar // "bbb"
    • 如果解构失败,变量的值等于undefined

    • 可以很方便地将现有对象的方法,赋值到某个变量

      • // 例二 const { log } = console; log('hello') // hello
    • 对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。前者是键是模式,后者是值是变量

      • let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" foo // error: foo is not defined
    • 解构也可以用于嵌套结构的对象

    • 对象的解构赋值可以取到继承的属性

    • 对象的解构也可以指定默认值

      • 默认值生效的条件是,对象的属性值严格等于undefined
    • 只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题,放在一个圆括号里面,就可以正确执行

    • 数组本质是特殊的对象,因此可以对数组进行对象属性的解构

      • let arr = [1, 2, 3]; let {0 : first, [arr.length - 1] : last} = arr; first // 1 last // 3
  • 基本类型

    • 字符串

      • 字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象
    • 数值 布尔值

      • 解构赋值时,如果等号右边是数值和布尔值,则会先转为对象
  • 函数参数

    • 函数的参数也可以使用解构赋值

    • 函数参数的解构也可以使用默认值

      • function move({x, y} = { x: 0, y: 0 }) { return [x, y]; }

move({x: 3, y: 8}); // [3, 8] move({x: 3}); // [3, undefined] move({}); // [undefined, undefined] move(); // [0, 0] - 1)函数参数不为undefined,所以不使用函数参数默认值,x=3,y=8; 2)函数参数不为undefined,所以不使用函数参数默认值,x=3,y=undefined; 3)函数参数不为undefined,所以不使用函数参数默认值,x=undefined,y=undefined; 4)函数参数为undefined,所以使用函数参数默认值,x=0,y=0;

  • 用途

    • 交换变量的值

    • 从函数返回多个值

      • 函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便
    • 函数参数的定义

      • 解构赋值可以方便地将一组参数与变量名对应起来
    • 提取 JSON 数据

      • 解构赋值对提取 JSON 对象中的数据,尤其有用

        • let jsonData = { id: 42, status: "OK", data: [867, 5309] };

let { id, status, data: number } = jsonData;

console.log(id, status, number); // 42, "OK", [867, 5309]

- 函数参数的默认值

	- 避免了在函数体内部再写var foo = config.foo || 'default foo';这样的语句

- 遍历 Map 结构

	- 部署了 Iterator 接口的对象,都可以用for...of循环遍历

		- const map = new Map();

map.set('first', 'hello'); map.set('second', 'world');

for (let [key, value] of map) { console.log(key + " is " + value); } // first is hello // second is world

- 输入模块的指定方法

	- 解构赋值使得输入语句非常清晰

		- const { SourceMapConsumer, SourceNode } = require("source-map");

四、控制语句

if

  • if(布尔值) 语句或语句块;

if...else

  • if(布尔值){语句}else{语句};

if...else if...else

for (初始化表达式; 条件; 递增表达式)

语句 // 或者 for (初始化表达式; 条件; 递增表达式) { 语句 }

  • 1)初始化表达式(initialize):确定循环变量的初始值,只在循环开始时执行一次。 2)条件表达式(test):每轮循环开始时,都要执行这个条件表达式,只有值为真,才继续进行循环。 3)递增表达式(increment):每轮循环的最后一个操作,通常用来递增循环变量。

  • for语句的三个部分(initialize、test、increment),可以省略任何一个,也可以全部省略。

  • for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域

    • for (let i = 0; i < 3; i++) { let i = 'abc'; console.log(i); } // abc // abc // abc

while (条件)

语句; // 或者 while (条件) 语句;

do

语句 while (条件); // 或者 do { 语句 } while (条件);

  • 与while循环类似,唯一的区别就是先运行一次循环体,然后判断循环条件。
  • 不管条件是否为真,do...while循环至少运行一次,这是这种结构最大的特点

for in

  • 使用in操作符
  • 用以枚举对象的所有属性,属性没有顺序,所以循环顺序不可预测

switch case

  • switch使用全等,传入true,会依次判断case

  • switch(表达式){case 表达式:....}

  • case语句块中的break不能少

  • 建议switch...case结构可以用对象结构代替

    • function doAction(action) { switch (action) { case 'hack': return 'hack'; case 'slash': return 'slash'; case 'run': return 'run'; default: throw new Error('Invalid action.'); } }
    • function doAction(action) { var actions = { 'hack': function () { return 'hack'; }, 'slash': function () { return 'slash'; }, 'run': function () { return 'run'; } };

    if (typeof actions[action] !== 'function') { throw new Error('Invalid action.'); }

    return actionsaction; }

三元运算符

  • (条件) ? 表达式1 : 表达式2

break语句和continue语句都具有跳转作用,可以让代码不按既有的顺序执行

  • break语句用于跳出代码块或循环。
  • continue语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环

label:

语句

  • 语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置。常与break语句和continue语句配合使用,跳出特定的循环或代码块

五、数据类型

JavaScript 语言的每一个值,都属于某一种数据类型

原始类型

基础类型

  • 基本类型:简单的数据段,undefined、null、boolean、number、string,按值访问,可以操作保存在变量中实际的值,栈内存

  • undefined

    • undefined是一个表示"此处无定义"的原始值,转为数值时为NaN
    • // 1)变量声明了,但没有赋值 var i; i // undefined

// 2)调用函数时,应该提供的参数没有提供,该参数等于 undefined function f(x) { return x; } f() // undefined

// 3)对象没有赋值的属性 var o = new Object(); o.p // undefined

// 4)函数没有返回值时,默认返回 undefined function f() {} f() // undefined

  • null

    • null是一个表示“空”的对象,转为数值时为0
  • boolean

    • 下列运算符会返回布尔值: 1)前置逻辑运算符: ! (Not) 2)相等运算符:===,!==,==,!= 3)比较运算符:>,>=,<,<=
    • 如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值,undefined、null、false、0、NaN、""或''(空字符串)六个值会转为false。注意,空数组([])和空对象({})对应的布尔值,都是true
  • number

    • JS内部,所有数字都是以64位浮点数形式储存,即使整数也是如此,底层根本没有整数,所有数字都是小数,浮点数不精确,涉及小数的计算和比较需要注意

      • 1 === 1.0 // true
    • 数值精度

      • (-1)^符号位 * 1.xx...xx * 2^指数部分
    • 数值范围

    • 数值进制

      • 使用字面量(literal)直接表示一个数值时,JS对整数提供四种进制的表示方法:十进制、十六进制、八进制、二进制: 1)十进制(Decimal):没有前导0的数值。 2)八进制(Octal):有前缀0o或0O的数值,且只用到0-7的八个阿拉伯数字的数值。 3)十六进制(Hex):有前缀0x或0X的数值。 4)二进制(Binary):有前缀0b或0B的数值。

        • 前导0表示八进制,处理时很容易造成混乱。ES5 的严格模式和 ES6,已经废除了这种表示法
      • 默认情况下,JS内部会自动将八进制、十六进制、二进制转为十进制

    • 特殊数值

      • 正零和负零

        • 除当做分母外不同(得到正负Infinity),其余时刻都等于正常0
      • NaN(Not a Number)

        • 表示一个本要返回数值的操作数未返回数值的情况,避免抛出错误。出现在将字符串解析成数字、数学函数的运算结果出错的场合

          • 5 - 'x' // NaN Math.acos(2) // NaN Math.log(-1) // NaN Math.sqrt(-1) // NaN 0 / 0 // NaN
        • 注意,NaN不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于number,使用typeof运算符可以看得很清楚。

          • typeof NaN // 'number'
        • NaN与任何数(包括它自己)的运算,得到的都是NaN,NaN不等于任何值,包括它本身

        • 涉及NaN的操作都返回NaN

        • NaN不与任何值相等,包括NaN,只能使用isNaN函数判断一个值是否为NaN

          • 对象的isNaN:先valueOf,再toString
      • Infinity

        • 表示“无穷”,表示两种场景。一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非0数值除以0,得到Infinity
    • 全局方法

      • parseInt()

        • 用于将字符串转为整数;

          • parseInt('123') // 123
        • 1)如果字符串头部有空格,空格会被自动去除;

          • parseInt(' 81') // 81
        • 2)如果parseInt的参数不是字符串,则会先转为字符串再转换;

          • parseInt(1.23) // 1 // 等同于 parseInt('1.23') // 1

parseInt(0x11, 36) // 43 parseInt(0x11, 2) // 1 // 等同于 parseInt(String(0x11), 36) parseInt(String(0x11), 2) // 等同于 parseInt('17', 36) parseInt('17', 2) 0x11先默认转为十进制17,再转为字符串

		- 3)字符串转为整数的时候,是一个个字符依次转换,如果遇到不能转为数字的字符,就不再进行下去,返回已经转好的部分;(Number函数是有非数字就NaN)

			- parseInt('8a') // 8

parseInt('12**') // 12 parseInt('12.34') // 12 parseInt('15e2') // 15 parseInt('15px') // 15

		- 4)如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN

			- parseInt('abc') // NaN

parseInt('.3') // NaN parseInt('') // NaN parseInt('+') // NaN parseInt('+1') // 1

		- 返回值只有两种可能,要么是一个十进制整数,要么是NaN
		- 5)如果字符串以0x或0X开头,parseInt会将其按照十六进制数解析;如果字符串以0开头,将其按照10进制解析
		- 进制转换:接受第二个参数(2到36之间),表示被解析的值的进制,返回该值对应的十进制数

			- parseInt('1000', 10) // 1000

parseInt('1000', 2) // 8 parseInt('1000', 6) // 216 parseInt('1000', 8) // 512

	- parseFloat() 

		- 用于将一个字符串转为浮点数
		- 如果字符串包含不能转为浮点数的字符,则不再进行往后转换,返回已经转好的部分
		- 自动过滤字符串前导的空格
		- 如果参数不是字符串,或者字符串的第一个字符不能转化为浮点数,则返回NaN

			- parseFloat([]) // NaN

parseFloat('FF2') // NaN parseFloat('') // NaN

	- isNaN()

		- 用来判断一个值是否为NaN

			- isNaN(NaN) // true

isNaN(123) // false

		- isNaN只对数值有效,如果传入其他值,会被先转成数值

			- isNaN('Hello') // true

// 相当于 isNaN(Number('Hello')) // true

		- 但是,对于空数组和只有一个数值成员的数组,isNaN返回false

	- isFinite()

		- 返回一个布尔值,表示某个值是否为正常的数值
  • string

    • 字符串就是零个或多个排在一起的字符,放在单引号或双引号之中

    • 反斜杠(\)在字符串内有特殊含义,用来表示一些特殊字符,所以又称为转义符

    • 字符串可以被视为字符数组

      • 可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始)
      • 如果方括号中的数字超过字符串的长度,或者方括号中根本不是数字,则返回undefined
      • 实际上,无法改变字符串之中的单个字符
    • length属性返回字符串的长度,该属性也是无法改变的

    • 每个字符在 JavaScript 内部都是以16位(即2个字节)的 UTF-16 格式储存。也就是说,JS的单位字符长度固定为16位长度,即2个字节

    • Base64 转码

      • 文本里面包含一些不可打印的符号,比如 ASCII 码0到31的符号都无法打印出来,这时可以使用 Base64 编码,将它们转成可以打印的字符。另一个场景是,有时需要以文本格式传递二进制数据,那么也可以使用 Base64 编码

      • Base64 就是一种编码方法,可以将任意值转成 0~9、A~Z、a-z、+和/这64个字符组成的可打印字符。使用它的主要目的,不是为了加密,而是为了不出现特殊字符,简化程序的处理

      • 原生方法

        • btoa():任意值转为 Base64 编码(原生)

        • atob():Base64 编码转为原来的值(原生)

        • 注意,这两个方法不适合非 ASCII 码的字符,会报错。要将非 ASCII 码字符转为 Base64 编码,必须中间插入一个转码环节,再使用这两个方法

          • function b64Encode(str) { return btoa(encodeURIComponent(str)); }

function b64Decode(str) { return decodeURIComponent(atob(str)); }

b64Encode('你好') // "JUU0JUJEJUEwJUU1JUE1JUJE" b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"

合成类型

引用类型

  • 引用类型:object、array、function,多个值构成的对象,js不允许直接访问内存地址,只能操作对象的引用,按引用访问,堆内存

  • object

    • 简单说,对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合

      • var obj = { foo: 'Hello', bar: 'World' };
    • 对象的所有键名都是字符串(数值会被自动转为字符串,其他类型会报错)

    • 如果键名是数值,会被自动转为字符串,注意js中其他进制会被自动转为十进制

      • var obj = { 1: 'a', 3.2: 'b', 1e2: true, 1e-2: true, .234: true, 0xFF: true };

obj // Object { // 1: "a", // 3.2: "b", // 100: true, // 0.01: true, // 0.234: true, // 255: true // }

- 对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用

	- 属性可以动态创建,不必在对象声明时就指定

- 1)不同的变量名指向同一个对象,则都是这个对象的引用,即指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。

2)两个变量指向同一个原始类型的值。那么,变量这时都是值的拷贝 - 无法确定是对象还是代码块,一律解释为代码块。如果要解释为对象,最好在大括号前加上圆括号。圆括号的里面,只能是表达式,所以确保大括号只能解释为对象

	- ({ foo: 123 }) // 正确

({ console.log(123) }) // 报错

- 属性操作

	- 读取

		- 1)点运算符

2)方括号运算符

			- var obj = {

p: 'Hello World' }; obj.p // "Hello World" obj['p'] // "Hello World"

		- 方括号运算符内部使用表达式。

所以,键名必须放在引号里面,否则会被当作变量处理。 数字键可以不加引号,会自动转成字符串

			- obj['hello' + ' world']

obj[3 + 3]

		- 数值键名不能使用点运算符(会被当成小数点),只能使用方括号运算符 (数组也是对象)

			- var obj = {

123: 'hello world' }; obj.123 // 报错 obj[123] // "hello world"

	- 赋值

		- 点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值

			- var obj = {};

obj.foo = 'Hello'; obj['bar'] = 'World';

		- JS允许属性的“后绑定”,也就是说,你可以在任意时刻新增属性

	- 查看

		- 查看一个对象本身的所有属性,可以使用Object.keys方法

			- var obj = {

key1: 1, key2: 2 }; Object.keys(obj); // ['key1', 'key2']

	- 删除

		- delete命令用于删除对象的属性,删除成功后返回true

			- var obj = { p: 1 };

Object.keys(obj) // ["p"] delete obj.p // true obj.p // undefined Object.keys(obj) // []

		- 删除一个不存在的属性,delete不报错,而且返回true
		- 只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除

			- var obj = Object.defineProperty({}, 'p', {

value: 123, configurable: false });

obj.p // 123 delete obj.p // false

		- delete命令只能删除对象本身的属性,无法删除继承的属性

	- 是否存在

		- in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值)

			- var obj = { p: 1 };

'p' in obj // true 'toString' in obj // true

		- in运算符不能识别哪些属性是对象自身的,哪些属性是继承的
		- 使用对象的hasOwnProperty方法判断是否为对象自身的属性

			- var obj = {};

if ('toString' in obj) { console.log(obj.hasOwnProperty('toString')) // false }

	- 遍历

		- for...in循环用来遍历一个对象的全部属性:

1)遍历对象所有可遍历(enumerable)属性,跳过不可遍历的属性 2)遍历对象自身的属性和继承的属性 - 遍历对象自身的属性,使用for...in的时候,应该结合hasOwnProperty方法,在循环内部判断某个属性是否为对象自身的属性

  • function

    • 函数是一段可以反复调用的代码块,函数名是指针,所以没有重载

    • 声明

      • 1)function命令声明的代码区块,就是一个函数。函数提升,整个函数会像变量声明一样,被提升到代码头部。但如果采用赋值语句定义函数,JS会报错。

        • f(); var f = function (){}; // TypeError: undefined is not a function ==> var f; f(); f = function () {};
      • 2)函数表达式:将一个匿名函数赋值给变量。这个匿名函数又称函数表达式(Function Expression),因为赋值语句的等号右侧只能放表达式。function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效

        • var print = function(s) { console.log(s); };
      • 如果同一个函数被多次声明,后面的声明就会覆盖前面的声明,由于函数名的提升,前一次声明在任何时候都是无效的

    • 圆括号运算符:函数名后面紧跟一对圆括号,就会调用/执行这个函数

    • return语句,表示返回。return语句不是必需的,如果没有的话,该函数就不返回任何值,或者说返回undefined

    • 一等公民:JS语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。函数只是一个可以执行的值,此外并无特殊之处。

    • 属性/方法

      • name属性返回函数的名字

        • 通过变量赋值定义的函数,那么name属性返回变量名;
        • 如果变量的值是一个具名函数,那么name属性返回function关键字之后的那个函数名
      • length属性返回函数预期传入的参数个数,即函数定义之中的参数个数,不管调用时输入了多少个参数,函数声明时确定

      • toString方法返回一个字符串,内容是函数的源码

        • 原生函数toString()方法返回function (){[native code]}
    • 函数作用域

      • 作用域(scope)指的是变量存在的范围

      • 对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量

      • 函数作用域内部也会产生“变量提升”现象

      • 函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。函数体内部声明的函数,作用域绑定函数体内部

        • var a = 1; var x = function () { console.log(a); };

function f() { var a = 2; x(); }

f() // 1

	- 执行环境、

变量对象、 作用域链

		- 执行环境(excutiosn context)定义变量或函数有权访问的其他数据,每个函数都有自己的执行环境
		- 变量对象(variable object)每个执行环境都有一个与之关联的变量对象,保存执行环境中的变量和函数
		- 当执行流进入一个函数的时候,函数的执行环境就会被压进一个环境栈,函数执行结束,栈弹出其环境,将控制权交给之前的执行环境
		- 作用域链(scope chain)保证对执行环境有权访问的变量和函数的有序访问,其前端始终是当前执行环境的变量对象,下一个变量对象来自包含环境(外部),直到全局执行环境(作用域链的最后一个对象)
		- 标识符的解析是沿着作用域链一级级的解析,从前端开始,直到全局执行环境

- 参数

	- 函数参数不是必需的,JS允许省略参数,省略的参数值变为undefined,函数的length属性与实际传入的参数个数无关,只反映函数预期传入的参数个数

		- 没有办法只省略靠前的参数,而保留靠后的参数。如果一定要省略靠前的参数,只有显式传入undefined

	- 传递方式

		- 原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value),函数体内修改参数值,不会影响到函数外部
		- 复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。传入函数的原始值的地址,在函数内部修改参数,会影响到原始值

			- 如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。因为这时是重新赋值,函数参数在函数括号中时声明

	- 如果有同名的参数,则取最后出现的那个值
	- arguments

		- JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数
		- arguments对象包含了函数运行时的所有参数
		- 这个对象只有在函数体内部,才可以使用
		- 正常模式下,arguments对象可以在运行时修改;严格模式下,arguments对象与函数参数不具有联动关系
		- length属性,可以判断函数调用时到底带几个参数,因为arguments对象在运行时才会创建,函数运行时确定
		- 像数组,但它是一个对象,可以将arguments转为真正的数组

			- var args = Array.prototype.slice.call(arguments);

// 或者 var args = []; for (var i = 0; i < arguments.length; i++) { args.push(arguments[i]); }

		- callee属性,返回它所对应的原函数,严格模式下禁用

- 闭包(Closure)

	- 能够读取其他函数内部变量的函数,简单理解成“定义在一个函数内部的函数”
	- 最大的特点,就是它可以“记住”诞生的环境;本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁
	- 注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大

- 立即调用的函数表达式(IIFE)

Immediately-Invoked Function Expression

	- JavaScript 引擎规定,如果function关键字出现在行首,一律解释成语句
  • array

    • 按次序排列的一组任何类型数据,每个值的位置都有编号(从0开始),整个数组用方括号表示

    • 本质上,数组属于一种特殊的对象(特殊在键都是数字,而数字会自动转为字符串)。typeof运算符会返回数组的类型是object

    • length属性

      • 返回数组的成员数量,是一个动态的值,等于键名中的最大整数数字键加上1
      • 数组的数字键不需要连续,length属性的值总是比最大的那个整数键大1
      • 数组是一种动态的数据结构,可以随时增减数组的成员
      • length属性是可写的。如果设置一个小于当前成员个数的值,数组成员会自动减少到length设置的值
      • 清空数组的一个有效方法,就是将length属性设为0。
      • 当length属性设为大于数组个数时,读取新增的位置都会返回undefined。(相当于声明了,但未赋值)
    • 可以为数组添加属性,但是这不影响length属性的值

    • in 运算符

      • 检查某个键名是否存在的运算符in,适用于对象,也适用于数组,in操作符针对对象的键/属性
    • 数组遍历

      • for...in循环不仅可以遍历对象,也可以遍历数组,毕竟数组只是一种特殊对象,但是会遍历到数组中后添加的属性,所以不推荐

        • var a = [1, 2, 3]; a.foo = true;

for (var key in a) { console.log(key); } // 0 // 1 // 2 // foo

	- 使用for循环或while循环
	- forEach方法,也可以用来遍历数组

		- var colors = ['red', 'green', 'blue'];

colors.forEach(function (color) { console.log(color); }); // red // green // blue

- 空位

	- 当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)
	- 空位不影响length属性
	- 是可以读取的,返回undefined
	- 使用delete命令删除一个数组成员,会形成空位,并且不会影响length属性

		- var a = [1, 2, 3];

delete a[1];

a[1] // undefined a.length // 3

	- 某个位置是空位,与某个位置是undefined,是不一样的

		- 如果是空位,使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历,空位都会被跳过。
		- 如果某个位置是undefined,遍历的时候就不会被跳过。

- 类数组对象

array-like object

	- 如果一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组
	- “类似数组的对象”并不是数组,因为它们不具备数组特有的方法
	- 根本特征,就是具有length属性。只要有length属性,就可以认为这个对象类似于数组,但是这个length不是动态的
	- 典型的“类似数组的对象”是函数的arguments对象,以及大多数 DOM 元素集,还有字符串
	- 转变成真正的数组

		- var arr = Array.prototype.slice.call(arrayLike);

- 扩展运算符...

	- rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列
	- 只有函数调用时,扩展运算符才可以放在圆括号中
	- 扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了

		- // ES5 的写法

function f(x, y, z) { // ... } var args = [0, 1, 2]; f.apply(null, args);

// ES6的写法 function f(x, y, z) { // ... } let args = [0, 1, 2]; f(...args); - // ES5 的写法 Math.max.apply(null, [14, 3, 77])

// ES6 的写法 Math.max(...[14, 3, 77])

// 等同于 Math.max(14, 3, 77);

	- 复制数组

		- 数组是复合数据类型/引用类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组
		- const a1 = [1, 2];

// 写法一 const a2 = [...a1]; // 写法二 const [...a2] = a1;

	- 合并数组

		- const arr1 = ['a', 'b'];

const arr2 = ['c']; const arr3 = ['d', 'e'];

// ES5 的合并数组 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]

	- 与解构赋值结合
	- 将字符串转为真正的数组

类型判断

  • typeof运算符

    • typeof 123 // "number" typeof '123' // "string" typeof false // "boolean" function f() {} typeof f // "function" typeof undefined // "undefined" typeof window // "object" typeof {} // "object" typeof [] // "object" typeof null // "object"
    • 可以用来检查一个没有声明的变量,而不报错,在判断语句中使用
  • instanceof运算符

  • Object.prototype.toString方法

    • const TYPE = {};

for (let i = 0, type; type = ["Boolean", "String", "Number", "Array", "Function", "Object"][i++];) { TYPE[is${type}] = obj => Object.prototype.toString.call(obj) === [object ${type}]; }

TYPE.isNumber(1) // true

  • 确定基本类型使用typeof,确定引用类型使用instanceof(不精确)

类型转换

  • JavaScript 是一种动态类型语言,变量没有类型限制,可以随时赋予任意值

  • 变量的数据类型是不确定的,但是各种运算符对数据类型是有要求的。如果运算符发现,运算子的类型与预期不符,就会自动转换类型

  • 强制转换

    • Number()

      • 将任意类型的值转化成数值(转换成原始类型)

      • 原始类型

        • 1)数值:转换后还是原来的值 Number(324) // 324

2)字符串:如果可以被解析为数值,则转换为相应的数值 Number('324') // 324

3)字符串:如果不可以被解析为数值,返回 NaN Number('324abc') // NaN

4)空字符串转为0 Number('') // 0

5)布尔值:true 转成 1,false 转成 0 Number(true) // 1 Number(false) // 0

6)undefined:转成 NaN Number(undefined) // NaN

7)null:转成0 Number(null) // 0 - Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。 - parseInt逐个解析字符,而Number函数整体转换字符串的类型 - parseInt和Number函数都会自动过滤一个字符串前导和后缀的空格

	- 引用类型

		- 简单的规则是,Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组
		- 第一步,调用对象自身的valueOf方法。如果返回原始类型的值,则直接对该值使用Number函数,不再进行后续步骤。

第二步,如果valueOf方法返回的还是对象,则改为调用对象自身的toString方法。如果toString方法返回原始类型的值,则对该值使用Number函数,不再进行后续步骤。

第三步,如果toString方法返回的是对象,就报错。

valueOf() ==> toString()

- String()

	- 将任意类型的值转化成字符串
	- 原始类型

		- 1)数值:转为相应的字符串。

2)字符串:转换后还是原来的值。 3)布尔值:true转为字符串"true",false转为字符串"false"。 4)undefined:转为字符串"undefined"。 5)null:转为字符串"null"。

	- 引用类型

		- 参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式

			- String({a: 1}) // "[object Object]"

String([1, 2, 3]) // "1,2,3"

		- 1)先调用对象自身的toString方法。如果返回原始类型的值,则对该值使用String函数,不再进行以下步骤。

2)如果toString方法返回的是对象,再调用原对象的valueOf方法。如果valueOf方法返回原始类型的值,则对该值使用String函数,不再进行以下步骤。

3)如果valueOf方法返回的是对象,就报错。

toString() ==> valueOf()

- Boolean()

	- 将任意类型的值转为布尔值
	- 除了以下五个值的转换结果为false,其他的值全部为true。

undefined null 0(包含-0和+0) NaN ''(空字符串) - 注意,所有对象(包括空对象)的转换结果都是true,甚至连false对应的布尔对象new Boolean(false)也是true(所有对象的布尔值都是true)

  • 自动转换

    • 预期什么类型的值,就调用该类型的转换函数(强制转换的函数)

    • 场景: 1)不同类型的数据互相运算 2)对非布尔值类型的数据求布尔值 3)对非数值类型的值使用一元运算符(即+和-)

    • 自动转为布尔值

      • 除了以下五个值,其他都是自动转为true。 undefined null +0或-0 NaN ''(空字符串)
    • 自动转换字符串

      • 遇到预期为字符串的地方,就会将非字符串的值自动转为字符串。具体规则是,先将复合类型的值转为原始类型的值,再将原始类型的值转为字符串

      • 字符串的自动转换,主要发生在字符串的加法运算时。当一个值为字符串,另一个值为非字符串,则后者转为字符串。

        • '5' + 1 // '51' '5' + true // "5true" '5' + false // "5false" '5' + {} // "5[object Object]" '5' + [] // "5" '5' + function (){} // "5function (){}" '5' + undefined // "5undefined" '5' + null // "5null"
    • 自动转为数值

      • 自动调用Number函数
      • 除了加法运算符(+)有可能把运算子转为字符串,其他运算符都会把运算子自动转成数值
      • null转为数值时为0,而undefined转为数值时为NaN

六、运算符

算术

  • 加法

    • JavaScript 允许非数值的相加,加法运算符存在重载,可能执行两种运算

    • 布尔值都会自动转成数值,然后再相加

      • true + true // 2 1 + true // 2
    • 两个字符串相加,这时加法运算符会变成连接运算符,返回一个新的字符串,将两个原字符串连接在一起

      • 'a' + 'bc' // "abc"
    • 如果一个运算子是字符串,另一个运算子是非字符串,这时非字符串会转成字符串,再连接在一起

      • 1 + 'a' // "1a" false + 'a' // "falsea"
    • 其他算术运算符(比如减法、除法和乘法)都不会发生重载。它们的规则是:所有运算子一律转为数值,再进行相应的数学运算,注意NaN

    • 对象的相加:如果运算子是对象,必须先转成原始类型的值,然后再相加

    • 对象转成原始类型的值: 1)首先,自动调用对象的valueOf方法 2)再自动调用对象的toString方法,将其转为字符串

  • 求余

    • 返回前一个运算子被后一个运算子除,所得的余数

    • 运算结果的正负号由第一个运算子的正负号决定,为了得到负数的正确余数值,可以先使用绝对值函数

      • -1 % 2 // -1 1 % -2 // 1
  • 指数

    • 指数运算符(**)完成指数运算,前一个运算子是底数,后一个运算子是指数
    • 指数运算符是右结合,而不是左结合

比较

  • 非相等运算符

    • 字符串按照字典顺序进行比较,比较字符的 Unicode 码点

      • 'cat' > 'Cat' // true'
    • 原始类型值

      • 两个运算子都是原始类型的值,则是先转成数值再比较

        • 5 > '4' // true // 等同于 5 > Number('4') // 即 5 > 4
      • 任何值(包括NaN本身)与NaN比较,返回的都是false

    • 对象

      • 如果运算子是对象,会转为原始类型的值,再进行比较
      • 先调用valueOf方法;如果返回的还是对象,再接着调用toString方法
  • 相等运算符

    • 相等运算符(==)比较两个值是否相等

    • 原始类型的值会转换成数值再进行比较

      • 'true' == true // false // 等同于 Number('true') === Number(true) // 等同于 NaN === 1
    • 对象(这里指广义的对象,包括数组和函数)与原始类型的值比较时,对象转换成原始类型的值,再进行比较

    • undefined和null与其他类型的值比较时,结果都为false,它们互相比较时结果为true

    • 相等运算符隐藏的类型转换,会带来一些违反直觉的结果

      • 0 == '' // true 0 == '0' // true
  • 严格相等运算符 全等运算符

    • 严格相等运算符(===)比较它们是否为“同一个值”

    • 同一类型的原始类型的值(数值、字符串、布尔值)比较时,值相同就返回true,值不同就返回false

    • 两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个地址

      • {} === {} // false [] === [] // false (function () {} === function () {}) // false
      • 空对象、空数组、空函数的值,都存放在不同的内存地址,结果当然是false
    • 对于两个对象的比较,严格相等运算符比较的是地址,而大于或小于运算符比较的是值

    • undefined和null与自身严格相等

      • undefined === undefined // true null === null // true

var v1; var v2; v1 === v2 // true

布尔

    • 非布尔值,取反运算符会将其转为布尔值

      • 以下六个值取反后为true,其他值都为false: undefined null false 0 NaN 空字符串('')
    • 对一个值连续做两次取反运算,等于将其转为对应的布尔值,与Boolean函数的作用相同

      • !!x // 等同于 Boolean(x)
  • &&

    • 用于多个表达式的求值

    • 运算规则是:如果第一个运算子的布尔值为true,则返回第二个运算子的值(注意是值,不是布尔值);如果第一个运算子的布尔值为false,则直接返回第一个运算子的值,且不再对第二个运算子求值

      • if (i) { doSomething(); }

// 等价于

i && doSomething();

- 跳过第二个运算子的机制,被称为“短路”
- 多个连用时返回第一个布尔值为false的表达式的值。如果所有表达式的布尔值都为true,则返回最后一个表达式的值

	- 因为与表达式由第一个false决定
  • ||

    • 用于多个表达式的求值

    • 运算规则是:如果第一个运算子的布尔值为true,则返回第一个运算子的值,且不再对第二个运算子求值;如果第一个运算子的布尔值为false,则返回第二个运算子的值

    • 跳过第二个运算子的机制,被称为“短路”

    • 多个连用时返回第一个布尔值为true的表达式的值。如果所有表达式都为false,则返回最后一个表达式的值

      • 或表达式有第一个true决定
  • ?:

    • 三元条件运算符由问号(?)和冒号(:)组成,分隔三个表达式

二进制位

  • 二进制位运算符用于直接对二进制位进行计算

  • 直接处理每一个比特位(bit),非常底层的运算,好处是速度极快

  • 位运算符只对整数起作用,如果一个运算子不是整数,会自动转为整数后再执行

  • 在 JavaScript 内部,数值都是以64位浮点数的形式储存,但是做位运算的时候,是以32位带符号的整数进行运算的,并且返回值也是一个32位带符号的整数

    • i = i | 0; //将i(不管是整数或小数)转为32位整数
  • |

    • 或运算符(|)逐位比较两个运算子,两个二进制位之中只要有一个为1,就返回1,否则返回0
  • &

    • 与运算符(&)逐位比较两个运算子,两个二进制位之中只要有一个位为0,就返回0,否则返回1
  • ~

    • 否运算符(~)将每个二进制位都变为相反值(0变为1,1变为0)
    • 对一个整数连续两次二进制否运算,得到它自身;对一个小数连续进行两次二进制否运算,能达到取整效果(取整方法中最快的一种)
    • 对其他类型进行二进制否运算,JavaScript 引擎会先调用Number函数,将字符串转为数值
  • ^

    • 异或运算(^)在两个二进制位不同时返回1,相同时返回0(相异则或)

    • 连续对两个数a和b进行三次异或运算,a^=b; b^=a; a^=b;,可以互换它们的值(最快方法)

    • 异或运算也可以用来取整

      • 12.9 ^ 0 // 12
  • <<

    • 左移运算符(<<)表示将一个数的二进制值向左移动指定的位数,尾部补0,即乘以2的指定次方
    • 向左移动的时候,最高位的符号位是一起移动的
    • 如果左移0位,就相当于将该数值转为32位整数,等同于取整,对于正数和负数都有效
    • 右移运算符(>>)表示将一个数的二进制值向右移动指定的位数。如果是正数,头部全部补0;如果是负数,头部全部补1。右移运算符基本上相当于除以2的指定次方(最高位即符号位参与移动)。
    • 头部补零的右移运算符(>>>)与右移运算符(>>)只有一个差别,就是一个数的二进制形式向右移动时,头部一律补零,而不考虑符号位
    • 总是得到正值
  • 开关作用

逗号运算符

  • 逗号运算符用于对两个表达式求值,并返回后一个表达式的值

  • 一个用途是,在返回一个值之前,进行一些辅助操作

    • var value = (console.log('Hi!'), true); // Hi!

七、控制台

console

console对象是 JavaScript 的原生对象

用途

  • 调试程序,显示网页代码运行时的错误信息。
  • 提供了一个命令行接口,用来与网页代码互动

静态方法

  • log接受一个或多个参数,将它们连接起来输出

    • console.log('Hello World') // Hello World console.log('a', 'b', 'c') // a b c
  • info是console.log方法的别名,用法完全一样。只不过console.info方法会在输出信息的前面,加上一个蓝色图标

  • debug与log类似,在控制台输出调试信息。默认情况下,debug输出的信息不会显示,只有在打开显示级别在verbose的情况下,才会显示

  • console对象的所有方法,都可以被覆盖

    • ['log', 'info', 'warn', 'error'].forEach(function(method) { console[method] = console[method].bind( console, new Date().toISOString() ); });

console.log("出错了!"); // 2014-05-18T09:00.000Z 出错了!

  • warn和error也是在控制台输出信息 warn输出信息时,在最前面加一个黄色三角,表示警告 error输出信息时,在最前面加一个红色的叉,表示出错 同时,还会高亮显示输出文字和错误发生的堆栈

  • table方法可以将复合类型转为表格显示

  • count方法用于计数,输出它被调用了多少次

  • dir方法用来对一个对象进行检查(inspect),并以易于阅读和打印的格式显示

    • 对于输出 DOM 对象非常有用,因为会显示 DOM 对象的所有属性
    • Node 环境之中,还可以指定以代码高亮的形式输出
  • dirxml方法主要用于以目录树的形式,显示 DOM 节点

  • assert方法主要用于程序运行过程中,进行条件判断,如果不满足条件,就显示一个错误,但不会中断程序执行

    • 接受两个参数,第一个参数是表达式,第二个参数是字符串。只有当第一个参数为false,才会提示有错误,在控制台输出第二个参数,否则不会有任何结果
  • time、timeEnd方法用于计时,可以算出一个操作所花费的准确时间

    • time方法表示计时开始,timeEnd方法表示计时结束。它们的参数是计时器的名称。
  • trace方法显示当前执行的代码在堆栈中的调用路径

  • clear方法用于清除当前控制台的所有输出,将光标回置到第一行

debugger语句主要用于除错,作用是设置断点

  • Chrome 浏览器中,当代码运行到debugger语句时,就会暂停运行,自动打开脚本源码界面

八、标准库

Object对象

  • Object对象的原生方法分成两类:Object本身的方法与Object的实例方法

    • 访问属性或方法:点运算符、方括号运算符(其中传入字符串,即可以使用变量)
  • Object本身是一个函数,可以当作工具方法使用,将任意值转为对象。这个方法常用于保证某个值一定是对象。

    • 如果参数为空(或者为undefined和null),Object()返回一个空对象

      • var obj = Object(); // 等同于 var obj = Object(undefined); var obj = Object(null);

obj instanceof Object // true

- 如果参数是原始类型的值,Object方法将其转为对应的包装对象的实例
- 如果Object方法的参数是一个对象,它总是返回该对象,即不用转换
  • Object不仅可以当作工具函数使用,还可以当作构造函数使用,即前面可以使用new命令

    • 首要用途,是直接通过它来生成新对象

    • 注意,通过var obj = new Object()的写法生成新对象,与字面量的写法var obj = {}是等价的。或者说,后者只是前者的一种简便写法

    • 可以接受一个参数,如果该参数是一个对象,则直接返回这个对象;如果是一个原始类型的值,则返回该值对应的包装对象

      • var o1 = {a: 1}; var o2 = new Object(o1); o1 === o2 // true
  • 静态方法

    • 所谓“静态方法”,是指部署在Object对象自身的方法

    • keys方法的参数是一个对象,返回一个数组。该数组的成员都是该对象自身的(而不是继承的)所有属性名,只返回可枚举的属性

    • getOwnPropertyNames方法与Object.keys类似,也是接受一个对象作为参数,返回一个数组,包含了该对象自身的所有属性名,还返回不可枚举的属性名

    • getOwnPropertyDescriptor():获取某个属性的描述对象

    • defineProperty():通过描述对象,定义某个属性

    • defineProperties():通过描述对象,定义多个属性

    • Object.preventExtensions():防止对象扩展。

      • Object.isExtensible():判断对象是否可扩展。
    • Object.seal():禁止对象配置。

      • Object.isSealed():判断一个对象是否可配置。
    • Object.freeze():冻结一个对象。

      • Object.isFrozen():判断一个对象是否被冻结。
    • Object.create():该方法可以指定原型对象和属性,返回一个新的对象。

    • Object.getPrototypeOf():获取对象的Prototype对象。

  • 实例方法

    • 定义在Object.prototype对象,称为实例方法,所有Object的实例对象都继承了这些方法

    • Object.prototype.valueOf():返回当前对象对应的值。

      • 默认情况下返回对象本身
      • 自动类型转换时会默认调用这个方法
    • Object.prototype.toString():返回当前对象对应的字符串形式。

      • 返回一个对象的字符串形式,默认情况下返回类型字符串

      • 由于实例对象可能会自定义toString方法,覆盖掉Object.prototype.toString方法,所以为了得到类型字符串,最好直接使用Object.prototype.toString方法。通过函数的call方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型

        • 数值:返回[object Number]。
        • 字符串:返回[object String]。
        • 布尔值:返回[object Boolean]。
        • undefined:返回[object Undefined]。
        • null:返回[object Null]。
        • 数组:返回[object Array]。
        • arguments 对象:返回[object Arguments]。
        • 函数:返回[object Function]。
        • Error 对象:返回[object Error]。
        • Date 对象:返回[object Date]。
        • RegExp 对象:返回[object RegExp]。
        • 其他对象:返回[object Object]
    • Object.prototype.toLocaleString():返回当前对象对应的本地字符串形式。

    • Object.prototype.hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型

    • Object.prototype.isPrototypeOf():判断当前对象是否为另一个对象的原型。

    • Object.prototype.propertyIsEnumerable():判断某个属性是否可枚举。

属性描述符

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

  • 元属性

    • value

      • 是该属性的属性值
    • writable

      • 是一个布尔值,表示属性值(value)是否可改变(即是否可写),默认为true
      • 注意,正常模式下,对writable为false的属性赋值不会报错,只会默默失败。但是,严格模式下会报错,即使对a属性重新赋予一个同样的值
      • 如果原型对象的某个属性的writable为false,那么子对象将无法自定义这个属性,但是,通过覆盖属性描述对象,绕过这个限制,原型链会被完全忽视
    • enumerable

      • 是一个布尔值,表示该属性是否可遍历,默认为true
      • 如果一个属性的enumerable为false,下面三个操作不会取到该属性。 for..in循环 Object.keys方法 JSON.stringify方法
      • 如果需要获取对象自身的所有属性,不管是否可遍历,可以使用Object.getOwnPropertyNames方法
    • configurable

      • 是一个布尔值,表示可配置性,默认为true
      • configurable为false时,value、writable、enumerable和configurable都不能被修改了
      • 注意,writable只有在false改为true会报错,true改为false是允许的
      • 至于value,只要writable和configurable有一个为true,就允许改动
      • 可配置性决定了目标属性是否可以被删除(delete)
    • get

      • 是一个函数,表示该属性的取值函数(getter),默认为undefined
      • 取值函数get不能接受参数
    • set

      • 是一个函数,表示该属性的存值函数(setter),默认为undefined
      • 存值函数set只能接受一个参数(即属性的值)
  • Object.getOwnPropertyDescriptor()方法

    • 可以获取属性描述对象
    • 只能用于对象自身的属性,不能用于继承的属性
    • 第一个参数是目标对象,
    • 第二个参数是一个字符串,对应目标对象的某个属性名
  • Object.getOwnPropertyNames方法

    • 返回一个数组,成员是参数对象自身的全部属性的属性名,不管该属性是否可遍历
    • 跟Object.keys的行为不同,Object.keys只返回对象自身的可遍历属性的全部属性名
    • 也返回继承属性
  • Object.defineProperty()方法

    • 允许通过属性描述对象,定义或修改一个属性,然后返回修改后的对象,接受三个参数
    • object:属性所在的对象
    • propertyName:字符串,表示属性名
    • attributesObject:属性描述对象
    • var obj = Object.defineProperty({}, 'p', { value: 123, writable: false, enumerable: true, configurable: false });
  • Object.defineProperties()方法

    • 可以一次性定义或修改多个属性
    • var obj = Object.defineProperties({}, { p1: { value: 123, enumerable: true }, p2: { value: 'abc', enumerable: true }, p3: { get: function () { return this.p1 + this.p2 }, enumerable:true, configurable:true } });
    • Object.defineProperty()和Object.defineProperties()参数里面的属性描述对象,writable、configurable、enumerable这三个属性的默认值都为false
  • 一旦定义了取值函数get(或存值函数set),就不能将writable属性设为true,或者同时定义value属性,否则会报错

  • 实例对象的propertyIsEnumerable()方法返回一个布尔值,用来判断某个属性是否可遍历。注意,这个方法只能用于判断对象自身的属性,对于继承的属性一律返回false

  • 拷贝对象

    • 问题在于,如果遇到存取器定义的属性,会只拷贝值,可以通过Object.defineProperty方法来拷贝属性
    • var extend = function (to, from) { for (var property in from) { if (!from.hasOwnProperty(property)) continue; Object.defineProperty( to, property, Object.getOwnPropertyDescriptor(from, property) ); }

    return to; }

extend({}, { get a(){ return 1 } }) // { get a(){ return 1 } })

  • 控制状态

    • JavaScript 提供了三种冻结方法,最弱的一种是Object.preventExtensions,其次是Object.seal,最强的是Object.freeze

    • Object.preventExtensions方法可以使得一个对象无法再添加新的属性

      • Object.isExtensible方法用于检查一个对象是否使用了Object.preventExtensions方法
    • Object.seal方法使得一个对象既无法添加新属性,也无法删除旧属性

      • 只是禁止新增或删除属性,并不影响修改某个属性的值
      • Object.isSealed方法用于检查一个对象是否使用了Object.seal方法,这时,Object.isExtensible方法也返回false
    • Object.freeze方法可以使得一个对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量

      • Object.isFrozen方法用于检查一个对象是否使用了Object.freeze方法
      • 使用Object.freeze方法以后,Object.isSealed将会返回true,Object.isExtensible返回false
    • 漏洞:可以通过改变原型对象,来为对象增加属性

    • 局限是,如果属性值是对象,上面这些方法只能冻结属性指向的对象,而不能冻结对象本身的内容。

Array对象

  • 会自动扩容,当一个值放在超过数组长度的位置上,数组会重新计算长度,length属性可以设置(清空数组)

  • 构造函数

    • Array是 JavaScript 的原生对象,同时也是一个构造函数,可以用它生成新的数组

      • var arr = new Array(2); arr.length // 2
      • Array构造函数的参数2,表示生成一个两个成员的数组,每个位置都是空值
      • 如果没有使用new,运行结果也是一样的
      • Array作为构造函数,行为很不一致。不建议使用它生成新数组,直接使用数组字面量是更好的做法
      • // bad var arr = new Array(1, 2);

// good var arr = [1, 2];

  • 静态方法

    • isArray方法返回一个布尔值,表示参数是否为数组。它可以弥补typeof运算符的不足
  • 实例方法

    • valueOf方法

      • 是一个所有对象都拥有的方法,表示对该对象求值。不同对象的valueOf方法不尽一致,数组的valueOf方法返回数组本身
    • toString方法

      • 也是对象的通用方法,数组的toString方法返回数组的字符串形式
    • 插入 删除

      • push方法 尾入

        • 用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度
        • 改变原数组
      • pop方法 尾出

        • 用于删除数组的最后一个元素,并返回该元素。
        • 改变原数组
        • 对空数组使用pop方法,不会报错,而是返回undefined
        • push和pop结合使用,就构成了“后进先出”的栈结构(stack)
      • shift()方法 头出

        • 用于删除数组的第一个元素,并返回该元素。
        • 改变原数组
        • shift()方法可以遍历并清空一个数组
        • push()和shift()结合使用,就构成了“先进先出”的队列结构(queue)
      • unshift()方法 头入

        • 用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。
        • 改变原数组
      • splice()方法

        • 用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素组成的数组

          • 删除、插入、替换、截取
        • 改变原数组

        • 第一个参数是删除的起始位置(从0开始), 第二个参数是被删除的元素个数。 如果后面还有更多的参数,则表示这些就是要被插入数组的新元素

        • 起始位置如果是负数,就表示从倒数位置开始删除

        • 如果只是单纯地插入元素,splice方法的第二个参数可以设为0

        • 如果只提供第一个参数,等同于将原数组在指定位置拆分成两个数组(删除到最后一个元素)

    • 合并

      • join()方法 数组内

        • 以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔

        • 如果数组成员是undefined或null或空位,会被转成空字符串

        • 通过call方法,这个方法也可以用于字符串或类似数组的对象

          • Array.prototype.join.call('hello', '-') // "h-e-l-l-o"
      • concat方法 数组间

        • 用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变

          • ['hello'].concat(['world'], ['!']) // ["hello", "world", "!"]
        • concat也接受其他类型的值作为参数,添加到目标数组尾部

          • [].concat({a: 1}, {b: 2}) // [{ a: 1 }, { b: 2 }]
        • 如果数组成员包括对象,concat方法返回当前数组的一个浅拷贝

    • 排序

      • reverse方法

        • 用于颠倒排列数组元素,返回改变后的数组
        • 改变原数组
      • sort方法

        • 对数组成员进行排序,默认是按照字典顺序排序

        • 改变原数组

        • 按照自定义方式排序,可以传入一个函数作为参数

        • sort的参数函数本身接受两个参数,表示进行比较的两个数组成员。 如果该函数的返回值大于0,表示第一个成员排在第二个成员后面; 其他情况下,都是第一个元素排在第二个元素前面

          • 注意,自定义的排序函数最好返回数值
    • 提取

      • slice()方法

        • 用于提取目标数组的一部分,返回一个新数组,原数组不变

        • 第一个参数为起始位置(从0开始,会包括在返回的新数组之中), 第二个参数为终止位置(但该位置的元素本身不包括在内)。

        • 如果省略第二个参数,则一直返回到原数组的最后一个成员

        • 如果slice()方法的参数是负数,则表示倒数计算的位置

        • slice()没有参数,实际上等于返回一个原数组的拷贝

          • 重要应用,是将类似数组的对象转为真正的数组
    • 迭代: 1)在每一项元素上运行的函数 2)运行该函数的作用域对象(可选) 都不修改原数组

      • map

        • 返回函数返回值组成的数组
        • 1)接受一个函数作为第一个参数。该函数调用时,map方法向它传入三个参数:当前成员、当前位置和数组本身
        • 2)还可以接受第二个参数,用来绑定回调函数内部的this变量
        • 如果数组有空位,map方法的回调函数在这个位置不会执行,会跳过数组的空位
      • forEach

        • 对每个元素运行函数,没有返回值
        • 用法与map方法一致,参数是一个函数,该函数同样接受三个参数:当前值、当前位置、整个数组;接受第二个参数,绑定参数函数的this变量
        • forEach方法也会跳过数组的空位
      • filter

        • 返回函数返回值为true组成的数组
        • 用法与map方法一致,参数是一个函数,该函数同样接受三个参数:当前值、当前位置、整个数组;接受第二个参数,绑定参数函数的this变量
      • every

        • 函数返回值都为true,就为true
      • some

        • 函数返回值任一为true,就为true
    • 累积方法 reduce reduceRight

      • 依次处理数组的每个成员,最终累计为一个值,并返回该累积变量

      • 参数函数的四个参数: ·累积变量,默认为数组的第一个成员 ·当前变量,默认为数组的第二个成员 ·当前位置(从0开始)索引 ·原数组对象

        • 前两个是必须的,后两个则是可选的
      • 如果要对累积变量指定初值,可以把它放在reduce方法和reduceRight方法的第二个参数,第二个参数相当于设定了默认值,处理空数组时尤其有用

      • 还可以用来做一些遍历相关的操作。比如,找出字符长度最长的数组成员

        • function findLongest(entries) { return entries.reduce(function (longest, entry) { return entry.length > longest.length ? entry : longest; }, ''); }

findLongest(['aaa', 'bb', 'c']) // "aaa"

- 索引

	- indexOf方法

		- 返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1
		- 还可以接受第二个参数,表示搜索的开始位置

	- lastIndexOf方法

		- 返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1

- 链式使用

	- 上面这些数组方法之中,有不少返回的还是数组,所以可以链式使用

包装对象

  • 三种原始类型的值——数值、字符串、布尔值——在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”(wrapper)

    • var v1 = new Number(123); var v2 = new String('abc'); var v3 = new Boolean(true);

typeof v1 // "object" typeof v2 // "object" typeof v3 // "object"

v1 === 123 // false v2 === 'abc' // false v3 === true // false

  • “包装对象”,指的是与数值、字符串、布尔值分别相对应的Number、String、Boolean三个原生对象

  • 设计目的,首先是使得“对象”这种类型可以覆盖 JavaScript 所有的值,整门语言有一个通用的数据模型,其次是使得原始类型的值也有办法调用自己的方法

  • Number、String和Boolean这三个原生对象,如果不作为构造函数调用(即调用时不加new),而是作为普通函数调用,常常用于将任意类型的值转为数值、字符串和布尔值

    • // 字符串转为数值 Number('123') // 123

// 数值转为字符串 String(123) // "123"

// 数值转为布尔值 Boolean(123) // true

  • 实例方法

    • valueOf()方法

      • 返回包装对象实例对应的原始类型的值

        • new Number(123).valueOf() // 123 new String('abc').valueOf() // "abc" new Boolean(true).valueOf() // true
    • toString()方法

      • 返回对应的字符串形式

        • new Number(123).toString() // "123" new String('abc').toString() // "abc" new Boolean(true).toString() // "true"
  • 原始类型与实例对象 的自动转换

    • 某些场合,原始类型的值会自动当作包装对象调用,即调用包装对象的属性和方法。这时,JavaScript 引擎会自动将原始类型的值转为包装对象实例,并在使用后立刻销毁实例
    • 自动转换生成的包装对象是只读的,无法修改
    • 调用结束后,包装对象实例会自动销毁
  • 自定义方法

    • 在它的原型对象String.prototype上定义方法和属性,供原始类型的值直接调用

      • Number.prototype.double = function () { return this.valueOf() + this.valueOf(); };

(123).double() // 246

  • Boolean对象

    • 作为构造函数,它主要用于生成布尔值的包装对象实例

    • 注意,false对应的包装对象实例,布尔运算结果也是true

      • new Boolean(false);// true new Boolean(false).valueOf();// false
    • 作为类型转换函数

      • 可以单独使用,将任意值转为布尔值。这时Boolean就是一个单纯的工具方法
      • Boolean(undefined) // false Boolean(null) // false Boolean(0) // false Boolean('') // false Boolean(NaN) // false
      • Boolean(1) // true Boolean('false') // true Boolean([]) // true Boolean({}) // true Boolean(function () {}) // true Boolean(/foo/) // true
      • 使用双重的否运算符(!)也可以将任意值转为对应的布尔值
    • 对于一些特殊值,Boolean对象前面加不加new,会得到完全相反的结果,必须小心

  • Number对象

    • Number对象是数值对应的包装对象,可以作为构造函数使用,也可以作为工具函数使用

      • 作为构造函数时,它用于生成值为数值的对象
      • 作为工具函数时,它可以将任何类型的值转为数值
    • 静态属性

      • Number.POSITIVE_INFINITY

        • 正的无限,指向Infinity
      • Number.NEGATIVE_INFINITY

        • 负的无限,指向-Infinity
      • Number.NaN

        • 表示非数值,指向NaN
      • Number.MIN_VALUE

        • 表示最小的正数(即最接近0的正数,在64位浮点数体系中为5e-324),相应的,最接近0的负数为-Number.MIN_VALUE
      • Number.MAX_SAFE_INTEGER

        • 表示能够精确表示的最大整数,即9007199254740991
      • Number.MIN_SAFE_INTEGER

        • 表示能够精确表示的最小整数,即-9007199254740991
    • 实例方法

      • toString方法

        • 用来将一个数值转为字符串形式
        • 可以接受一个参数,表示输出的进制。如果省略这个参数,默认将数值先转为十进制,再输出字符串;否则,就根据参数指定的进制,将一个数字转化成某个进制的字符串
      • toFixed()方法

        • 先将一个数转为指定位数的小数,然后返回这个小数对应的字符串
        • 由于浮点数的原因,小数5的四舍五入是不确定的,使用的时候必须小心
      • toExponential方法

        • 用于将一个数转为科学计数法形式
      • toPrecision()方法

        • 用于将一个数转为指定位数的有效数字
        • 该方法用于四舍五入时不太可靠,跟浮点数不是精确储存有关
      • toLocaleString()方法

        • 接受一个地区码作为参数,返回一个字符串,表示当前数字在该地区的当地书写形式

          • (123).toLocaleString('zh-Hans-CN-u-nu-hanidec') // "一二三"
        • 如果style属性的值为currency,则可以搭配currency属性,输出指定格式的货币字符串形式

          • (123).toLocaleString('zh-Hans-CN', { style: 'currency', currency: 'CNY' }) // "¥123.00"
        • 省略了参数,则由浏览器自行决定如何处理,通常会使用操作系统的地区设定

      • 自定义方法

        • Number.prototype对象上面可以自定义方法,被Number的实例继承
  • String对象

    • 字符串对象是一个类似数组的对象(很像数组,但不是数组)

    • 除了用作构造函数,String对象还可以当作工具方法使用,将任意类型的值转为字符串

    • 静态方法

      • fromCharCode()方法

        • 参数是一个或多个数值,代表 Unicode 码点,返回值是这些码点组成的字符串
        • 不支持 Unicode 码点大于0xFFFF的字符
        • 根本原因在于,码点大于0xFFFF的字符占用四个字节,而 JavaScript 默认支持两个字节的字符
    • 实例属性

      • length属性返回字符串的长度
    • 实例方法

      • 字符

        • charAt方法

          • 返回指定位置的字符,参数是从0开始编号的位置
          • 完全可以用数组下标替代
          • 如果参数为负数,或大于等于字符串的长度,charAt返回空字符串
        • charCodeAt方法

          • 返回字符串指定位置的 Unicode 码点(十进制表示),相当于String.fromCharCode()的逆操作
          • 只返回两个字节的字符的码点
          • 如果遇到码点大于 65536 的字符(四个字节的字符),必需连续使用两次charCodeAt,不仅读入charCodeAt(i),还要读入charCodeAt(i+1),将两个值放在一起,才能得到准确的字符
      • 连接 分割

        • concat方法

          • 用于连接两个字符串,返回一个新字符串,不改变原字符串
          • 如果参数不是字符串,concat方法会将其先转为字符串,然后再连接
        • split方法

          • 按照给定规则分割字符串,返回一个由分割出来的子字符串组成的数组
          • 如果分割规则为空字符串,则返回数组的成员是原字符串的每一个字符
          • 如果满足分割规则的两个部分紧邻着(即两个分割符中间没有其他字符),则返回数组之中会有一个空字符串
          • 如果满足分割规则的部分处于字符串的开头或结尾(即它的前面或后面没有其他字符),则返回数组的第一个或最后一个成员是一个空字符串
          • 还可以接受第二个参数,限定返回数组的最大成员数
      • 提取

        • slice方法

          • 用于从原字符串取出子字符串并返回,不改变原字符串。
          • 第一个参数是子字符串的开始位置 第二个参数是子字符串的结束位置(不含该位置) 左闭右开
          • 如果省略第二个参数,则表示子字符串一直到原字符串结束
          • 如果参数是负值,表示从结尾开始倒数计算的位置,即该负值加上字符串长度
          • 如果第一个参数大于第二个参数,slice方法返回一个空字符串
        • substring方法

          • 用于从原字符串取出子字符串并返回,不改变原字符串,跟slice方法很相像
          • 第一个参数是子字符串的开始位置 第二个参数是子字符串的结束位置(不含该位置) 左闭右开
          • 如果省略第二个参数,则表示子字符串一直到原字符串的结束
          • 如果第一个参数大于第二个参数,substring方法会自动更换两个参数的位置
          • 如果参数是负数,substring方法会自动将负数转为0
          • 不建议使用substring方法,应该优先使用slice
        • substr方法

          • 用于从原字符串取出子字符串并返回,不改变原字符串,跟slice和substring方法的作用相同
          • 第一个参数是子字符串的开始位置(从0开始计算) 第二个参数是子字符串的长度
          • 如果省略第二个参数,则表示子字符串一直到原字符串的结束
          • 如果第一个参数是负数,表示倒数计算的字符位置。如果第二个参数是负数,将被自动转为0,因此会返回空字符串
      • 索引

        • indexOf方法

          • 用于确定一个字符串在另一个字符串中第一次出现的位置,返回结果是匹配开始的位置。如果返回-1,就表示不匹配
          • 还可以接受第二个参数,表示从该位置开始向后匹配
        • lastIndexOf方法

          • 用法跟indexOf方法一致,主要的区别是lastIndexOf从尾部开始匹配,indexOf则是从头部开始匹配
          • 第二个参数表示从该位置起向前匹配
      • 转换

        • trim方法

          • 用于去除字符串两端的空格,返回一个新字符串,不改变原字符串
          • 去除的不仅是空格,还包括制表符(\t、\v)、换行符(\n)和回车符(\r)
        • toLowerCase方法

          • 用于将一个字符串全部转为小写
          • 返回一个新字符串,不改变原字符串
        • toUpperCase方法

          • 全部转为大写
          • 返回一个新字符串,不改变原字符串
      • 正则

        • match方法

          • 用于确定原字符串是否匹配某个子字符串,返回一个数组,成员为匹配的第一个字符串。如果没有找到匹配,则返回null
          • 返回的数组还有index属性和input属性,分别表示匹配字符串开始的位置和原始字符串
        • search方法

          • 用法基本等同于match,但是返回值为匹配的第一个位置。如果没有找到匹配,则返回-1
        • replace方法

          • 用于替换匹配的子字符串,一般情况下只替换第一个匹配(除非使用带有g修饰符的正则表达式)
      • 比较

        • localeCompare方法

          • 用于比较两个字符串,返回一个整数, 如果小于0,表示第一个字符串小于第二个字符串; 如果等于0,表示两者相等; 如果大于0,表示第一个字符串大于第二个字符串

源自: wangdoc.com/ 、 红宝书 、 es6.ruanyifeng.com/