数据类型、对象从属的判断、类属性和实例属性、严格模式、属性读写、属性标签、数组、上下文管理、属性标签、函数属性、闭包、继承。
数据类型
- number
- string
- boolean
- null
- undefined
- Object
- Function
- Array
- Date
- Number
- …
对象从属的判断
typeof
1 | > typeof 123 |
instanceof
1 | > new Number(123) instanceof Number |
*. instanceof
不能跨 window
或 iframe
。
Object.prototype.toString.apply()
1 | > Object.prototype.toString.apply([]) |
类属性和实例属性
1 | function Foo(){} |
严格模式
arguments
、eval
均为保留字
属性读写
不同的属性读写方式
1 | var obj = {x:1, y:2}; |
链式属性查找
1 | var yz = obj && obj.y && obj.y.z |
继承属性和自有属性
辨析: in
操作符、hasOwnProperty
方法。
1 | var cat = new Object; |
getter() 和 setter()
1 | var person = { |
1 | function foo(){} |
JSON 序列化
正向序列化: JSON.stringify(注意奇怪的拼写)
1 | var obj1 = {x:1, y:[1,2,3], nullVal:null, undefinedVal:undefined} |
反向序列化: JSON.parse
1 | var json_string = '{"x":1,"y":[1,2,3],"nullVal":null}' |
自定义强制转换规则
toString
和 valueOf
1 | var obj = {x:1, y:2}; |
属性标签
Object.defineProperty()
感觉是个大学问,日后完善
1 | var obj = new Object(); |
属性标签探测
1 | > Object.getOwnPropertyDescriptor({pro: true}, 'pro') |
属性标签固定
1 | Object.preventExtenions(obj) |
数组
arr.length
求长,arr.length -= 1
操作可以移除最后一个元素。
*. delete 操作使得某元素的位置变为 undefined,但数组长度不会变。
arr.push() / arr.pop()
arr.push()
等价于 arr[arr.length]=
arr.unshift() / arr.shift()
在数组头部插入 / 删除
arr.join()
和 python 里的用法反一下。
1 | function repeatString(str, n) { |
arr.reverse()
同 python,略。
arr.sort()
如果 arr 内的元素是数字,会按照字符串来排序(并不是按大小)。
1 | arr.sort(function(a, b){return a-b}) |
arr.forEach()
1 | arr.forEach( |
arr.slice()
略
arr.splice(a, b, *args)
从原数组第 a 位起挖去 b 位返回,并重新插入 *args 个元素。
arr.map() / arr.filter() / arr.reduce()
同 python,略。
arr.every() / arr.some()
1 | var arr = [1, 2, 3, 4, 5] |
arr.indexOf(a, b) / lastIndexOf()
原数组从左往右(/右往左)的第 b 个元素开始,查找第一个 a 元素的位置。
上下文管理
with
注意: js 严格模式下禁用 with
块,可以使用变量来代替。
1 | with ({x:1}){ |
1 | with (document.forms[0]){ |
this
通常 指代容器所在的外层空间(爸爸的爸爸)。
1 | this; |
1 | o = {f: function(){return this.a+this.b}} |
闭包中的 this 指向 window
1 | var test = { |
在这种情况下,我们希望 getA() 和 getB() 返回的值是 test.a 和 test.b ,但是此时闭包函数(即函数中的函数)getA 和 getB 中 this 并不指向 test 的实例,该怎么办呢?我们不妨试试下面的方法:
1 | var test = { |
在 test 对象的 sum 函数中用一个局部变量 self 来保存当前的 this 指针,这样在闭包函数 getA 和 getB 中就能通过 self 变量获取 test 实例的属性了。
看起来这样就能够解决闭包函数中 this 的问题了,但是,如果调用 sum 函数的并不是 test 的实例呢,这个时候
var self=this
还能起到作用,获取到 test 的实例吗?
为了更方便地改变函数上下文(context)的空间,我们使用 call()
/ apply()) / apply()
/ apply()) / bind()
等函数(见下)。
call() / apply()
这两例中也需要格外注意 this
的用法。
用法: 调用一个对象的一个方法,以另一个对象替换当前对象。
1 | function add(c, d) { return this.a + this.b + c + d } |
1 | function bar(){ |
bind()
定义: 将接受多个参数的函数变换成接受一个单一参数(定义时不传参,调用时再传参)。
说明: bind() 方法所返回的函数的 length(形参数量)等于原函数的形参数量减去传入 bind() 方法中的实参数量(第一个参数以后的所有参数),因为传入 bind 中的实参都会绑定到原函数的形参。
1 | function f(){ return this.a } |
*. 扩展:高阶实例
1 | <button id="btn">烦人的this</button> |
这里我们就能发现问题所在了,当ID为btn的按钮被点击时会触发test.sum函数,但是这个时候的this=button,而且参数a、b如何传入呢?
这里就能够使用bind函数了,将test.sum函数简化为另一个新的函数,同时传入参数a和b,我们再看看下面的代码:
1 | <button id="btn">this</button> |
从上面的代码我们可以看到test.sum.bind(test,4,5)返回一个新的函数function(event),test、4、5分别被绑定到test.sum的上下文、参数a、参数b中。
当ID为btn的按钮被点击时会触发test.sum函数,此时改函数中的this=test,a=4,b=5。
这样就可以解决事件绑定时的this以及传参的问题了,包括现在常用js框架中的事件绑定,如jQuery、signals.min.js等等。
*. 扩展:this 的指向
本实例以下内容来源:js: this,call,apply,bind 总结
JavaScript 中的 this 总是指向一个对象,而具体指向那个对象是在运行时基于函数的执行环境动态绑定的,而非函数声明时的环境。实际应用中 this 的指向大致可以分为以下 4 种:
- 作为对象的方法调用
- 作为普通函数掉用
- 构造器调用
- Function.prototype.call 或 Function.prototype.apply 调用, 可以动态地改变出入函数的 this
作为对象的方法调用时, this 指向该对象
1 | var obj = { |
作为普通函数掉用,this 总是指向全局对象 window
1 | console.log(this); // Windows |
作为构造器调用,指向返回的新对象
当用 new 运算符调用函数时,该函数总是会返回一个对象,通常情况下,构造函数里的 this 就指向返回的这个对象。
1 | var MyClass = function(){ |
如果使用 new 调用构造器时,构造器显式地返回了一个 object 类型的对象,那么此次运算结果最终会返回这个对象,而不是我么之前期待的 this。
1 | var MyClass = function(){ |
*. 再扩展
最后贴一个我还没看完的似乎很棒的长文:详解JS中的this、apply、call、bind(经典面试题)
函数属性
1 | function foo(x, y, z){ |
闭包
一个典型的错误例子:
1 | document.body.innerHTML = "<div id=div1> aaa </div>" |
运行后,控制台输出“1 ↙ 2 ↙ 3”,但点击div时,alert 的内容均为4。
运用【立即执行】的匿名函数实现预期功能:
1 | document.body.innerHTML = "<div id=div1> aaa </div>" |
立即执行(IIFE)
- 限定变量作用范围(私有属性的实现);
- 避免污染全局变量;
- 闭包变量的固定;
以下内容摘自:JavaScript中立即执行函数实例详解
因为IIFE通常用于匿名函数,这里就用简单的匿名函数作为栗子:
1 | var f = function(){ |
我们发现这里f只是这个匿名函数的一个引用变量,那么既然f()能够调用这个函数,我把f替换成函数本身可以么:
1 | function(){ |
运行之后得到如下结果:
1 | Uncaught SyntaxError: Unexpected token ( |
产生这个错误的原因是,Javascript引擎看到function关键字之后,认为后面跟的是函数声明语句,不应该以圆括号结尾。解决方法就是让引擎知道,圆括号前面的部分不是函数定义语句,而是一个表达式,可以对此进行运算,这里区分一下函数声明和函数表达式:
1 | //用小括号把函数包裹起来 |
函数成功执行了:
1 | f //控制台输出 |
这种情况下Javascript引擎就会认为这是一个表达式,而不是函数声明,当然要让Javascript引擎认为这是一个表达式的方法还有很多:
1 | !function(){}(); |
作用域链
函数内层可以访问外层变量,外层不能访问内层。
继承
1 | function Person(name, age){ |
实现继承的几种方式
1 | Student.prototype = Person.prototype; |
链式调用
return this
1 | function ClassManager(){} |
上面的代码改写如下,效果完全相同:
1 | function ClassManager(){ |
正则表达式
表示方法
- /\d\d\d/
- RegExp(‘\d\d\d’)
匹配
1 | > /\d\d\d/.test(123); |
三个 flag
- g - global
- i - ignoreCase
- m - multiline
1 | /\d\d\d/mgi.test(123); |
字符串用法
- search
- test
- replace
- match
- split
1 | > 'aabbbccbbd'.match(/b+/); |