JS粗读笔记(乱七八糟一锅炖版一)

本文是我看阮一峰前辈的博客时自己随手搞得摘要,就是给自己看的,所以没什么质量可言

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

for...in循环有两个使用注意点。
  • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性
  • 它不仅遍历对象自身的属性,还遍历继承的属性。

对象有两种读取成员的方法:“点”结构( object.key)和方括号结构( object[key])。但是,对于数值的键名,不能使用点结构。

数组

由于数组本质上是对象的一种,所以我们可以为数组添加属性,但是这不影响 length属性的值。

数组的 slice方法将类似数组的对象,变成真正的数组。
var arr = Array.prototype.slice.call(arrayLike);
var arr = [];arr[100] = 'a';100 in arr // true1 in arr // false

 
   

检查某个键名是否存在的运算符 in,适用于对象,也适用于数组。
var arr = [];arr[100] = 'a';100 in arr // true1 in arr // false



for...in循环不仅可以遍历对象,也可以遍历数组,毕竟数组只是一种特殊对象。但是, for...in不仅会遍历数组所有的数字键,还会遍历非数字键。

使用 delete命令删除一个数组成员,会形成空位,并且不会影响 length属性。
数组的某个位置是空位,与某个位置是 undefined,是不一样的。如果是空位,使用数组的 forEach方法、 for...in结构、以及 Object.keys方法进行遍历,空位都会被跳过。
函数
(1)function命令
function命令声明的代码区块,就是一个函数。 function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。
function print(s) {  console.log(s);}


(2)函数表达式
除了用 function命令声明函数,还可以采用变量赋值的写法。
var print = function(s) {  console.log(s);};


采用函数表达式声明函数时, function 命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。

需要注意的是,函数的表达式需要在语句的结尾加上分号,表示语句结束。而函数的声明在结尾的大括号后面不用加分号。

如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。由于函数名的提升(参见下文),前一次声明在任何时候都是无效的,这一点要特别注意。

由于“变量提升”,函数 f被提升到了代码头部,也就是在调用之前已经声明了。但是,如果采用赋值语句定义函数,JavaScript就会报错。如果同时采用 function命令和赋值语句声明同一个函数,最后总是采用赋值语句的定义。

根据ECMAScript的规范,不得在非函数的代码块中声明函数,最常见的情况就是if和try语句

name属性返回紧跟在 function关键字之后的那个函数名。
length属性返回函数预期传入的参数个数,即函数定义之中的参数个数。
函数的 toString方法返回函数的源码。

作用域(scope)指的是变量存在的范围。Javascript只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。

与全局作用域一样,函数作用域内部也会产生“变量提升”现象。 var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。

函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

很容易犯错的一点是,如果函数 A调用函数 B,却没考虑到函数 B不会引用函数 A的内部变量。

函数参数不是必需的,Javascript允许省略参数。

通过下面的方法,可以为函数的参数设置默认值
function f(a) {  (a !== undefined && a !== null) ? a = a : a = 1;  return a;}f() // 1f('') // ""f(0) // 0

如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。
var obj = [1, 2, 3];function f(o){  o = [2, 3, 4];}f(obj);obj // [1, 2, 3]


如果有同名的参数,则取最后出现的那个值。
function f(a, a) {  console.log(a);}f(1, 2) // 2

上面的函数 f 有两个参数,且参数名都是 a 。取值的时候,以后面的 a 为准。即使后面的 a 没有值或被省略,也是以其为准。

需要注意的是,虽然 arguments很像数组,但它是一个对象。数组专有的方法(比如 sliceforEach),不能在 arguments对象上直接使用。
但是,可以通过 apply方法,把 arguments作为参数传进去,这样就可以让 arguments使用数组方法了。
// 用于apply方法myfunction.apply(obj, arguments).// 使用与另一个数组合并Array.prototype.concat.apply([1,2,3], arguments)

要让arguments对象使用数组方法,真正的解决方法是将arguments转为真正的数组。下面是两种常用的转换方法:slice方法和逐一填入新数组。
var args = Array.prototype.slice.call(arguments);// orvar args = [];for (var i = 0; i < arguments.length; i++) {  args.push(arguments[i]);}

arguments 对象带有一个 callee 属性,返回它所对应的原函数。

如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。

闭包最大的特点,就是它可以“记住”诞生的环境,比如 f2记住了它诞生的环境 f1,所以从 f2可以得到 f1的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。

解决方法就是不要让 function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。
(function(){ /* code */ }());// 或者(function(){ /* code */ })();

上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称IIFE。
注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个IIFE,可能就会报错。

eval命令的作用是,将字符串当作语句执行。
放在 eval中的字符串,应该有独自存在的意义,不能用来与 eval以外的命令配合使用。
JavaScript规定,如果使用严格模式, eval内部声明的变量,不会影响到外部作用域。

运算符
+
  1. 如果运算子是对象,先自动转成原始类型的值(即先执行该对象的valueOf方法,如果结果还不是原始类型的值,再执行toString方法;如果对象是Date实例,则先执行toString方法)。
  2. 两个运算子都是原始类型的值以后,只要有一个运算子是字符串,则两个运算子都转为字符串,执行字符串连接运算。
  3. 否则,两个运算子都转为数值,执行加法运算。

加法运算符以外的其他算术运算符(比如减法、除法和乘法),都不会发生重载。它们的规则是:所有运算子一律转为数值,再进行相应的数学运算。


数值运算符( +)同样使用加号,但是加法运算符是二元运算符(需要两个操作数),它是一元运算符(只需要一个操作数)。
数值运算符的作用在于可以将任何值转为数值(与 Number函数的作用相同)。
+true // 1+[] // 0+{} // NaN数值运算符号和负数值运算符,都会返回一个新的值,而不会改变原始变量的值。


字符串按照字典顺序进行比较。
undefinednull与其他类型的值比较时,结果都为 false,它们互相比较时结果为 true
绝大多数情况下,对象与 undefinednull比较,都返回 false。只有在对象转为原始值得到 undefined时,才会返回 true,这种情况是非常罕见的。

对于非布尔值的数据,取反运算符会自动将其转为布尔值。规则是,以下六个值取反后为 true,其他值取反后都为 false
  • undefined
  • null
  • false
  • 0(包括+0-0
  • NaN
  • 空字符串(''

如果对一个值连续做两次取反运算,等于将其转为对应的布尔值,与 Boolean函数的作用相同。这是一种常用的类型转换的写法。

有一点需要特别注意,位运算符只对整数起作用,如果一个运算子不是整数,会自动转为整数后再执行。另外,虽然在JavaScript内部,数值都是以64位浮点数的形式储存,但是做位运算的时候,是以32位带符号的整数进行运算的,并且返回值也是一个32位带符号的整数。

所有的位运算都只对整数有效。否运算遇到小数时,也会将小数部分舍去,只保留整数部分。所以,对一个小数连续进行两次否运算,能达到取整效果。
使用否运算取整,是所有取整方法中最快的一种。
对字符串进行否运算,JavaScript引擎会先调用Number函数,将字符串转为数值。
Number函数将字符串转为数值的规则,参见《数据的类型转换》一节。否运算对特殊数值的处理是:超出32位的整数将会被截去超出的位数,NaN和Infinity转为0。
对于其他类型的参数,否运算也是先用 Number转为数值,然后再进行处理。

“异或运算”有一个特殊运用,连续对两个数a和b进行三次异或运算,aˆ=b, bˆ=a, aˆ=b,可以互换它们的值(详见 维基百科)。这意味着,使用“异或运算”可以在不引入临时变量的前提下,互换两个变量的值。
这是互换两个变量的值的最快方法。

函数放在圆括号中,会返回函数本身。如果圆括号紧跟在函数的后面,就表示调用函数。

数据类型转换
原始类型的值主要是字符串、布尔值、 undefinednull,它们都能被 Number转成数值或 NaN
// 数值:转换后还是原来的值Number(324) // 324// 字符串:如果可以被解析为数值,则转换为相应的数值Number('324') // 324// 字符串:如果不可以被解析为数值,返回NaNNumber('324abc') // NaN// 空字符串转为0Number('') // 0// 布尔值:true 转成1,false 转成0Number(true) // 1Number(false) // 0// undefined:转成 NaNNumber(undefined) // NaN// null:转成0Number(null) // 0

Number 函数将字符串转为数值,要比 parseInt 函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为 NaN
parseInt('42 cats') // 42Number('42 cats') // NaN

上面代码中, parseInt 逐个解析字符,而 Number 函数整体转换字符串的类型。
另外, Number函数会自动过滤一个字符串前导和后缀的空格。
Number('\t\v\r12.34\n') // 12.34


实际上, Number背后的真正规则复杂得多,内部处理步骤如下。
  1. 调用对象自身的valueOf方法。如果返回原始类型的值,则直接对该值使用Number函数,不再进行后续步骤。
  2. 如果valueOf方法返回的还是对象,则改为调用对象自身的toString方法。如果返回原始类型的值,则对该值使用Number函数,不再进行后续步骤。
  3. 如果toString方法返回的是对象,就报错。

使用 String函数,可以将任意类型的值转化成字符串。转换规则如下。
(1)原始类型值的转换规则
  • 数值:转为相应的字符串。
  • 字符串:转换后还是原来的值。
  • 布尔值true转为"true"false转为"false"
  • undefined:转为"undefined"
  • null:转为"null"

String方法背后的转换规则,与 Number方法基本相同,只是互换了 valueOf方法和 toString方法的执行顺序。
  1. 先调用对象自身的toString方法。如果返回原始类型的值,则对该值使用String函数,不再进行以下步骤。
  2. 如果toString方法返回的是对象,再调用valueOf方法。如果返回原始类型的值,则对该值使用String函数,不再进行以下步骤。
  3. 如果valueOf方法返回的是对象,就报错。

使用 Boolean函数,可以将任意类型的变量转为布尔值。
它的转换规则相对简单:除了以下六个值的转换结果为 false,其他的值全部为 true
  • undefined
  • null
  • -0
  • 0+0
  • NaN
  • ''(空字符串)

注意,所有对象(包括空对象)的转换结果都是 true,甚至连 false对应的布尔对象 new Boolean(false)也是 true

遇到以下三种情况时,JavaScript会自动转换数据类型,即转换是自动完成的,对用户不可见。
// 1. 不同类型的数据互相运算123 + 'abc' // "123abc"// 2. 对非布尔值类型的数据求布尔值if ('abc') {  console.log('hello')}  // "hello"// 3. 对非数值类型的数据使用一元运算符(即“+”和“-”)+ {foo: 'bar'} // NaN- [1, 2, 3] // NaN

自动转换的规则是这样的:预期什么类型的值,就调用该类型的转换函数。比如,某个位置预期为字符串,就调用 String 函数进行转换。如果该位置即可以是字符串,也可能是数值,那么默认转为数值。
由于自动转换具有不确定性,而且不易除错,建议在预期为布尔值、数值、字符串的地方,全部使用 BooleanNumberString函数进行显式转换。

JavaScript 编程风格
  1. 表示函数调用时,函数名与左括号之间没有空格。
  2. 表示函数定义时,函数名与左括号之间没有空格。
  3. 其他情况时,前面位置的语法元素与左括号之间,都有一个空格。

JavaScript最大的语法缺点,可能就是全局变量对于任何一个代码块,都是可读可写。这对代码的模块化和重复使用,非常不利。
因此,避免使用全局变量。如果不得不使用,用大写字母表示变量名,比如 UPPER_CASE

为了避免可能出现的问题,最好把变量声明都放在代码块的头部。

建议使用 Object.create()命令,替代 new命令。如果不得不使用 new,为了防止出错,最好在视觉上把构造函数与其他函数区分开来。比如,构造函数的函数名,采用首字母大写(InitialCap),其他函数名一律首字母小写。

因此,不要使用 with语句。

建议避免使用 switch...case结构,用对象结构代替
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 actions[action]();}


Object对象
Object作为构造函数使用时,可以接受一个参数。如果该参数是一个对象,则直接返回这个对象;如果是一个原始类型的值,则返回该值对应的包装对象。

注意,通过 new Object()的写法生成新对象,与字面量的写法 o = {}是等价的。

上面代码表示 Object函数可以将各种值转为对应的构造函数生成的对象。
如果 Object方法的参数是一个对象,它总是返回原对象
利用这一点,可以写一个判断变量是否为对象的函数。
function isObject(value) {  return value === Object(value);}isObject([]) // trueisObject(true) // false


Object.keys 方法和 Object.getOwnPropertyNames 方法很相似,一般用来遍历对象的属性。它们的参数都是一个对象,都返回一个数组,该数组的成员都是对象自身的(而不是继承的)所有属性名。它们的区别在于, Object.keys 方法只返回可枚举的属性(关于可枚举性的详细解释见后文), Object.getOwnPropertyNames 方法还返回不可枚举的属性名。

Object.prototype.valueOf()
valueOf方法的作用是返回一个对象的“值”,默认情况下返回对象本身。

Object.prototype.toString()
toString方法的作用是返回一个对象的字符串形式,默认情况下返回类型字符串。
不同数据类型的 Object.prototype.toString方法返回值如下。
  • 数值:返回[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 " + 构造函数的名称 + "]


你可能感兴趣的:(JavaScript入门)