一、变量提升
console.log(a); //undefined var a = 0;
以上代码段并不会报错,js中存在变量提升,会将变量的声明提升至文件的顶部,形式如下。
var a; console(a); a = l;
函数会不会存在变量提升呢?答案是显然的。
f(); function f(){}
表面上好像是f()在声明前已经被调用了,由于变量提升f()被提升到了代码头部,但是采用复制语句定义函数会报错。
f(); var f = function(){}; //TypeError: undefined is not a function /* because: var f; f(); f = function(){} */
二、label
var a = 0; loop: for(let i=0;i<10;i++){ for(let j=0;j<10;j++){ a++; break loop; } } console.log(a); //1
以上loop为一个label,label多用来break或continue跳出代码块。
三、null与undefined
null与undefined在if语句中被转换为false。
null是一个表示为“空”的对象,它的出生较早(1995),根据C语言的传统,可以自动转换为0.
Number(null); //0 null+5; //5
undefined表明此处无定义的原始值
Number(undefined); //NaN undefined+6; //NaN
如果JavaScript预期某个位置应该是布尔值,以下值会转换为false: undefined, null, false, 0, NaN, ""或''(空字符串);而其他值都是为true.
NaN不是独立的数据类型,是一个特殊的数值,数据类型仍然属于Number。
四、console
var languages = [ { name: "JavaScript", fileExtension: ".js" }, { name: "TypeScript", fileExtension: ".ts" }, { name: "CoffeeScript", fileExtension: ".coffee" } ]; console.table(languages);
console.table()可以将对象以表格的形式打印。
console.time(), console.timeEnd()计算时间
五、defineProperty
var extend1 = function (to, from) { for (var property in from) { if (!from.hasOwnProperty(property)) continue; Object.defineProperty( to, property, Object.getOwnPropertyDescriptor(from, property) ); } return to; } var extend2 = function (to, from) { for (var property in from) { to[property] = from[property]; } return to; } var bak1 = {}; var bak2 = {}; extend1(bak1, { a:1, get getter(){ return this.a }, set setter(a) {console.log("a"+a)} }); extend2(bak2, { a:1, get getter(){ return this.a }, set setter(a) {console.log("a"+a)} }); console.dir(bak1); console.dir(bak2);
extend1,extend2分别为两个对象copy的方法,而extend2无法copy存储器定义的属性(只能copy到它的值),所以我们要采用extend1来copy对象,结果如下:
六、对象的冻结
对象的冻结有三种方法:
- Object.preventExtensions(obj) //该方法冻结后,对象无法添加新属性
- Object.isExtensible(obj) //该方法用来检验对象是否采用上诉方法冻结
- Object.seal(obj) //该方法冻结后,对象无法添加新属性,无法删除旧属性(实质是把对象的configurable设为false)
- Object.isSealed(obj) //检查该对象是否采用上诉方法冻结
- Object.freeze(obj) //无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量
- Object.isFrozen(obj) //检查该对象是否采用上诉方法冻结
局限性:可以通过改变原型改变对象(可以把原型也冻住),如果该对象是数组,只是冻结了数组,没有冻结数组的内容。
六、数组
你想在JS中形成栈结构吗?很简单,数组+push()+pop()可以实现
var array = []; array.push('a'); array.push('b'); array.pop(); //a
队列:数组+push()+shift()
七、this对象
一谈起this,我们都知道是再说当前对象。但是当前对象又总是显得那么抽象,所以理解起来也显得有些无力了。
那么this到底是什么东西,我觉得可以理解为当前(runtime)的一个环境,举例如下:
var a=1; var f = function(){ console.log(this.a); } var obj = { a:2, f:f }; f(); //1 obj.f(); //2
先说直接调用f(),因为对于f来讲,f在全局环境执行;而对于obj.f()来讲,obj.f的执行是在obj对象的内部环境,所以导致了结果的不同。
八、call, apply, bind
JS提供了call, apply, bind来切换this的指向。
call不传入参数或传入undifined,null。则默认传入全局值(window)。
var n = 123; var obj = { n: 456 }; function a() { console.log(this.n); } a.call() // 123 a.call(null) // 123 a.call(undefined) // 123 a.call(window) // 123 a.call(obj) // 456
func.call(thisValue, arg1, arg2, ...):在thisValue的上下文环境中调用func, arg1, arg2...是func所需参数
call常见套路:
var obj = {}; Object.prototype.hasOwnProperty.call(obj, 'toString') // false
将hasOwnProperty
方法的原始定义放到obj
对象上执行,这样无论obj
上有没有同名方法,都不会影响结果。
apply与call相似,不过func.apply(thisValue, [arg1, arg2, ...])的参数是一个数组。
bind方法用于将函数体内的this绑定到一个对象上。因为bind方法每次会返回一个新方法,如果没有变量名来接收它,则会产生一个匿名函数,这样就会出现一些奇怪的问题
e.g.:如果通过element.addEventListener("click",o.m.bind(o));来添加click监听则会出现remove不了监听的情况发生。
九、JS的模块封装
可以利用JS的立即执行函数起着封装模块的作用,代码如下:
(function($, window, document) { function go(num) { } function handleEvents() { } function initialize() { } function dieCarouselDie() { } //attach to the global scope window.finalCarousel = { init : initialize, destroy : dieCarouselDie } })( jQuery, window, document );
通过finalCarousel对象将init,destory暴露出来,其余内部方法无法访问。
十、proxy
proxy用于修改某些操作的默认行为,在语言层面上作出修改。可以理解成,在目标之前设立了“拦截”,外界对该对象的访问都必须先通过这层拦截。
var object = { proxy: new Proxy(target, handler) };
如果handler上没有设置任何拦截,等同于直接通向原对象。
,