自运行函数的返回
// 假设output输出一段文本
output(typeof (function() {output(“Hello World!”)})()) // Hello World! undefined
自运行函数首先运行输出Hello World
,然后自运行函数并没有return
,返回为undefined
,typeof undefined = undefined
。
for
循环和隐式转换
var msg = 'hello';
for (var i=0; i<10; i++){
var msg = 'hello' + i * 2 + i;
}
alert(msg); // hello189
for
循环中var
又声明了一个msg
变量,运行时msg
call和apply参数区分
var a = [1, 2, 3];
Math.max.apply(null, a); // 3
apply
接受一个数组作为传入的参数,Math.max
计算的则是一个数组中的最大值,(apply
是a
开头,array
也是a开头)。
循环稀疏数组
[, 1, 3, , 3].map(i => ++i); // [empty, 2, 4, empty, 4]
因为元素为empty
,所以不会循环到,也不会执行++i
。
构造函数的返回以及封装对象
'foo' === new function(){ return new String('foo'); }; // true
'foo' === new function(){ return String('foo'); }; // false
new
操作符后面是一个匿名的作为构造调用的函数,该函数返回一个对象new String('foo')
,而在与字符串foo
比较时,引擎会将foo
封装成字符串对象,所以相比较为true
,String('foo')
返回的则是单纯的字符串,所以相比较为false
。
JavaScript内置对象
函数属性
eval();
uneval();
isFinite();
isNaN();
parseFloat();
parseInt();
decodeURL();
decodeURIComponent();
encodeURI();
encodeURIComponent();
// 已废弃
escape();
unescape();
全局函数可以直接调用,不需要在调用时指定所属对象,执行结束后会将结果直接返回给调用者。
use strict模式
严格模式下,禁止this
指向全局对象。
function Foo() {
"use strict"
console.log(this.location); // this: undefined
}
Foo(); // TypeError
区分递增(递减)运算符后置(前置)
var a = 1;
var b = a++;
console.log(a); // 2
console.log(b); // 1
后置先赋值,后自增,记忆方式是变量在前,先将变量的值赋值给赋值运算符的左手边,再自增。
构造函数的返回
如果在构造函数中return
一个基本类型值,则使用new
运算符对构造函数进行构造调用时,赋值给变量的仍是对象,该对象的__proto__
指向构造函数的prototype
属性。
function B() {
return 1
}
const b = new B();
typeof b; // object
函数的length
属性
(() => {}).length; // 0
参数列表的长度。
阻止浏览器默认行为
// IE
window.event.returnValue = false;
// 其它
event.preventDefault();
页面加载顺序
闭包01
function test() {
var n = 4399;
function add() {
n++;
console.log(n);
}
return { n, add };
}
var result = test();
var result2 = test();
result.add(); // 4400
result.add(); // 4401
console.log(result.n); // 4399
result2.add(); // 4400
这个题的误区主要有两点:
- 这里有两个闭包。
- 函数
test
返回对象中的属性n
是一个基础类型值,不是引用,与闭包没有关系,执行n++
它的值是不会发生改变的。
内存堆栈与闭包
- 尽可能地拷贝对象而不是传递引用,只在对象较大且拷贝代价也大时才传递引用。
- 尽可能避免对象操作,相反,使用对象扩展或
Object.assign
来复制它们。 - 避免对同一个对象创建多个引用,相反,应拷贝一份这个对象。
- 使用短暂存活的变量。
- 避免创建嵌套过深的对象,如果无法避免,记得在当前作用域及时清理它们。
- 避免从栈变量引用堆对象,同时,切勿保留未使用的变量。
- 使用解构从对象或数组中获取需要的字段,而不是将整个对象或数组传递给函数、闭包、定时器或事件处理函数。这避免了闭包保留一个对象的引用。获取的字段往往都是原始值,而原始值是存放在栈中的。
内存泄露的几个原因
- 全局变量:因为
JavaScript
中的全局变量被根节点(window或global this)引用,所以它们在整个应用生命周期中不会被回收,即会一直占用内存。这同样也适用于那些被全局变量(或其子属性)引用的对象,通过根节点引用数量庞大的对象可能导致内存泄露。 - 多个引用:当同一个对象被多个对象引用时,当其中一个引用被挂起,可能会导致内存泄露。
- 闭包:
JavaScript
必败有一个很酷的特性,就是能够保存被它关联的上下文,当一个闭包持有一个引用,该引用指向堆中的一个庞大对象时,闭包会令该庞大对象持续在内存中直到闭包不再使用它。这意味着你可能会轻易地陷入这种情况。即持有着一个引用的闭包被不正确的使用从而导致内存泄露。 - 定时器&事件:使用
setTimeout
、setInterval
、Observers
和事件监听器时,如果没有妥当处理保存在它们回调函数中的庞大对象引用时,可能会导致内存泄漏。