前端:JavaScript补充 | 青训营笔记

112 阅读3分钟

前端:JavaScript编码原则 | 青训营笔记

这是我参与「第五届青训营 」伴学笔记创作活动的第 18 天

内容

this 指向问题

this 指代当前的对象,也就是调用了函数的对象。如果在一个对象上使用点或者方括号来访问属性或方法,这个对象就成了 this。如果并没有使用“点”运算符调用某个对象,那么 this 将指向全局对象(global object)。这是一个经常出错的地方。

case1:

function makePerson(first, last) {
    return {
        first: first,
        last: last,
        fullName: function() {
            return this.first + ' ' + this.last;
        },
        fullNameReversed: function() {
            return this.last + ', ' + this.first;
        }
    }
}
s = makePerson("Simon", "Willison");
s.fullName(); // "Simon Willison"
s.fullNameReversed(); // Willison, Simon

case2:

s = makePerson("Simon", "Willison");
var fullName = s.fullName;
fullName(); // undefined undefined

case2可以理解为 s = {…}; fullname = {…}.fullName;所以fullname是全局的函数名
再次调用时,fullname()<=>"window".fullname;所以this指向全局对象而报错
同理,case1就好理解了

总之,this始终指向调用它的对象

Person.prototype

Person.prototype 是一个可以被 Person 的所有实例共享的对象。它是一个名叫原型链(prototype chain)的查询链的一部分:当你试图访问 Person 某个实例(例如上个例子中的 s)一个没有定义的属性时,解释器会首先检查这个 Person.prototype 来判断是否存在这样一个属性。所以,任何分配给 Person.prototype 的东西对通过 this 对象构造的实例都是可用的。

这个特性功能十分强大,JavaScript 允许你在程序中的任何时候修改原型(prototype)中的一些东西,也就是说你可以在运行时 (runtime) 给已存在的对象添加额外的方法:

s = new Person("Simon", "Willison");
s.firstNameCaps();  // TypeError on line 1: s.firstNameCaps is not a function

Person.prototype.firstNameCaps = function() {
    return this.first.toUpperCase()
}
s.firstNameCaps(); // SIMON

avg.apply()

apply() 的第一个参数应该是一个被当作 this 来看待的对象。

下面是一个 new 方法的简单实现:

function trivialNew(constructor, ...args) {
    var o = {}; // 创建一个对象
    constructor.apply(o, args);
    return o;
}
var bill = trivialNew(Person, "William", "Orange"); == var bill = new Person("William", "Orange");

avg.call()

它也可以允许你设置 this,但它带有一个扩展的参数列表而不是一个数组。

function lastNameCaps() {
    return this.last.toUpperCase();
}
var s = new Person("Simon", "Willison");
lastNameCaps.call(s);
// 和以下方式等价
s.lastNameCaps = lastNameCaps;
s.lastNameCaps();

闭包

举例说明

// case1:普通函数
funciton add(a, b) {
    return a+b;
}
var sum = add(5, 20)

// case2:闭包
function makeAdder(a) {
  return function(b) {
    return a + b;
  }
}
var add5 = makeAdder(5);
var add20 = makeAdder(20);
add5(6); // ?
add20(7); // ?

case1:

image.png

case2:

补充概念

  1. 执行上下文
    创建阶段包括:作用域链、变量对象、this
    执行阶段包括:变量赋值、函数引用等
  2. 作用域链:当前变量对+所有父级变量对象
    变量对象:参数、变量

每当函数每当 JavaScript 执行一个函数时,都会创建一个作用域对象(scope object),用来保存在这个函数中创建的局部变量。它使用一切被传入函数的变量进行初始化(初始化后,它包含一切被传入函数的变量
当调用makerAdder时,a,这个属性被当作参数传入makeAdder 函数
然后makeAdder 返回一个新创建的函数(暂记为 adder)
通常,JavaScript 的垃圾回收器会在这时回收 makeAdder 创建的作用域对象(暂记为 b),但是,makeAdder 的返回值,新函数 adder,拥有一个指向作用域对象 b 的引用。最终,作用域对象 b 不会被垃圾回收器回收,直到没有任何引用指向新函数 adder。

参考文献:

  1. MDN Web Docs (mozilla.org)
  2. 前端与 HTML - 掘金 (juejin.cn)
  3. JavaScript闭包 - Web前端工程师面试题讲解_哔哩哔哩_bilibili

附:如有错误,恳请指正,侵权必删 :D