Skip to content

Instantly share code, notes, and snippets.

@jeasonstudio
Last active February 22, 2018 07:17
Show Gist options
  • Save jeasonstudio/04b9c6b49f4a8e560242139a9bb78edc to your computer and use it in GitHub Desktop.
Save jeasonstudio/04b9c6b49f4a8e560242139a9bb78edc to your computer and use it in GitHub Desktop.

简述 JavaScript 中的 this

JS 中的 this 是一个相对复杂的概念, 不是简单几句能解释清楚的. 比较不负责任的说法是 this 的值取决于函数是如何调用的. 我阅读了网上很多关于 this 的文章, Arnav Aggrawal 写的比较清楚. this 取值符合以下规则:

  1. 通过 new 关键字调用函数, this 会是一个全新的 object
  2. 使用 apply, call 或者 bind 来调用/创建函数, this 的值取决于传入的第一个参数
  3. 如果函数作为对象的方法被调用, 比如 obj.method(), this 的值为该函数所作为属性的 object, 比如 obj
  4. 如果函数调用时不满足上述条件, 也就是 free function, this 的值为全局对象. 浏览器环境下是 window 对象, 但是在严格模式下('use strict'), this 的值为 undefined
  5. ES2015(ES6) 提出的箭头函数(Arrow function)不符合上述规则, 箭头函数 this 的值是该函数被 创建时 的作用域

更深入的解释可以去查看他在 Medium 上的文章

References

null, undefined 和未定义的变量有什么区别? 如何区分?

当你先前没有使用 var, letconst 创建一个变量, 就直接给这个标识符(identifier, 或者说变量名?)赋值时, 你就会创建一个**未定义(Undeclared)**变量. 未定义变量不会存在于当前作用域, 而是默认定义在全局作用域(globally scope). 严格模式下, 你给一个未定义变量赋值时会抛出 ReferenceError 错误. 使用未定义变量不是好习惯, 这跟尽量不使用全局变量是一个道理, 应最大化的避免. 将代码包裹在 try/catch 中能帮你检查出未定义变量.

function foo() {
  x = 1; // Throws a ReferenceError in strict mode
}

foo();
console.log(x); // 1

undefined 指的是你定义了变量, 但没有赋值. 这个变量的类型就是 undefined. 如果函数没写返回值, 默认也会是 undefined. 检查变量是不是 undefined 需要用到严格等于(===)操作符或 typeof(foo === undefined or typeof foo === 'undefined'). 需要注意的是你不能用抽象相等操作符(==)来判断, 因为 null 值也会返回 true(null == undefined).

var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true

console.log(foo == null); // true. Wrong, don't use this to check!

function bar() {}
var baz = bar();
console.log(baz); // undefined

null 值只能是被显式赋值给变量. 它代表无意义或是空值, 并且和被显式赋值 undefined 的变量意义不同. 检查 null 值需要使用严格相等运算符.

var foo = null;
console.log(foo === null); // true

console.log(foo == undefined); // true. Wrong, don't use this to check!

作为一个好习惯, 你应该避免使用未定义或为未声明(undeclared or unassigned)的变量. 如果定义了暂时没有用到的变量,我会在声明后明确地给它们赋值为 null.

译者注: 这部分应借助一些工具比如代码检查 eslint, 静态类型检查: flow, typescript, 而不是刀耕火种

References

匿名函数有哪些使用场景?

它们可以用于立即执行函数(IIFE), 将代码密封到局部作用域, 使得其中声明的变量不会污染全局作用域.

(function() {
  // Some code here.
})();

作为一次使用的回调,不需要在其他地方使用. 当处理程序在调用它们的代码内部进行定义时,代码看起来更具自包含性和可读性,而不必在别处搜索以查找函数体. 作为一次性回调函数使用, 并且没有其他地方引用时, 建议用匿名函数. 这使得代码更具有自包含性和可读性(self-contained and readable).

setTimeout(function() {
  console.log('Hello world!');
}, 1000);

匿名函数也可以用于函数式编程结构的参数, 或 Lodash 方法的参数(类似回调函数).

const arr = [1, 2, 3];
const double = arr.map(function(el) {
  return el * 2;
});
console.log(double); // [2, 4, 6]

译者注: 其实匿名函数还有更多可细分的用处, 一般作为工程优化和最佳实践部分出现.

References

宿主对象(host objects)和本地对象(native objects)有什么区别?

ECMA-262 把本地对象(native object)定义为 独立于宿主环境的 ECMAScript 实现提供的对象, 比如 String, Math, RegExp, Object, Function 等等.

所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境(浏览器或者 nodejs)提供的对象, 比如 window, XMLHTTPRequest 等.

译者注: 这是一个有点冷门的概念, 除了宿主对象和本地对象, 还有一个常提到的内置对象(built-in object). ECMA-262 把内置对象定义为 由 ECMAScript 实现提供的, 独立于宿主环境的所有对象, 在 ECMAScript 程序开始执行时出现. 这意味着开发者不必明确实例化内置对象, 它已被实例化了.

References

什么时候会用到 document.write()?

document.write() 将一串文本写入由 document.open() 打开的文档流中. document.write() 在页面加载后执行, 清除整个 dom 树(包括 <head><body>), 并使用参数替换. 这是一个很危险也容易被误用的方法.

网上有一些说法是将它用于代码分析, 或者是某些特殊情况(比如你希望只有浏览器允许 JS 脚本执行时, 才显示样式), 也可以在 HTML5 中用于并行加载脚本并保留执行顺序. 但是我怀疑这些可能性都已经过时了, 现在我们不用 document.write() 也能实现目的.

译者注: 确实, 这个问题也只有在面试时才有可能被问到了.

References

ajax 技术有哪些优点和缺点?

优点

  • 交互性更好. 动态更改内容, 无需重新加载整个页面
  • 减少与服务器的连接, 因为 scriptsstylesheets 只需要被请求一次
  • Webapp 的状态可以维护在页面上, JavaScript 变量和 DOM 状态会一直保持
  • 基本上和 SPA 的优点一致

缺点

  • 动态网页不容易被收藏
  • 浏览器禁用 JavaScript 后, 页面不能正常访问
  • 有些网络爬虫不执行 JavaScript, 也不会看到动态加载的内容
  • 基本上和 SPA 的缺点一致

解释下变量提升(hoisting)

使用 var 关键字声明或初始化的变量, 会将 变量的声明 提升到当前作用域的顶部, 但是赋值(如果有变量赋值)的位置不变. 下面用几个例子说明一下.

// 用 var 声明变量会被提升
console.log(foo); // undefined
var foo = 1;
console.log(foo); // 1

// 使用 let/const 声明变量则不会被提升
console.log(bar); // ReferenceError: bar is not defined
let bar = 2;
console.log(bar); // 2

通过函数体声明的函数存在提升, 通过函数表达式(将函数声明为变量)声明的函数只有变量被提升.

// Function Declaration
console.log(foo); // [Function: foo]
foo(); // 'FOOOOO'
function foo() {
  console.log('FOOOOO');
}
console.log(foo); // [Function: foo]

// Function Expression
console.log(bar); // undefined
bar(); // Uncaught TypeError: bar is not a function
var bar = function() {
  console.log('BARRRR');
};
console.log(bar); // [Function: bar]

译者注: 这两句话着实难翻译, 还是看例子来的明白

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment