自运行函数的返回

// 假设output输出一段文本
output(typeof (function() {output(“Hello World!”)})())  // Hello World! undefined

自运行函数首先运行输出Hello World,然后自运行函数并没有return,返回为undefinedtypeof 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计算的则是一个数组中的最大值,(applya开头,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封装成字符串对象,所以相比较为trueString('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

这个题的误区主要有两点:

  1. 这里有两个闭包。
  2. 函数test返回对象中的属性n是一个基础类型值,不是引用,与闭包没有关系,执行n++它的值是不会发生改变的。

内存堆栈与闭包

  • 尽可能地拷贝对象而不是传递引用,只在对象较大且拷贝代价也大时才传递引用。
  • 尽可能避免对象操作,相反,使用对象扩展或Object.assign来复制它们。
  • 避免对同一个对象创建多个引用,相反,应拷贝一份这个对象。
  • 使用短暂存活的变量。
  • 避免创建嵌套过深的对象,如果无法避免,记得在当前作用域及时清理它们。
  • 避免从栈变量引用堆对象,同时,切勿保留未使用的变量。
  • 使用解构从对象或数组中获取需要的字段,而不是将整个对象或数组传递给函数、闭包、定时器或事件处理函数。这避免了闭包保留一个对象的引用。获取的字段往往都是原始值,而原始值是存放在栈中的

内存泄露的几个原因

  • 全局变量:因为JavaScript中的全局变量被根节点(window或global this)引用,所以它们在整个应用生命周期中不会被回收,即会一直占用内存。这同样也适用于那些被全局变量(或其子属性)引用的对象,通过根节点引用数量庞大的对象可能导致内存泄露
  • 多个引用:当同一个对象被多个对象引用时,当其中一个引用被挂起,可能会导致内存泄露。
  • 闭包:JavaScript必败有一个很酷的特性,就是能够保存被它关联的上下文,当一个闭包持有一个引用,该引用指向堆中的一个庞大对象时,闭包会令该庞大对象持续在内存中直到闭包不再使用它。这意味着你可能会轻易地陷入这种情况。即持有着一个引用的闭包被不正确的使用从而导致内存泄露。
  • 定时器&事件:使用setTimeoutsetIntervalObservers和事件监听器时,如果没有妥当处理保存在它们回调函数中的庞大对象引用时,可能会导致内存泄漏。