JavaScript 闭包行状录——从一道面试题开始说起

注意,在阅读这篇文章之前,你应该已经理解了 JavaScript 的 执行上下文变量对象作用域链 等概念。如果还有疑问的话,你可以阅读 这篇文章 的前三节。另外的,你也应该知道 JavaScript 是词法作用域。

本文的例子来源于 这篇文章,而他参考了 这篇文章 。你可以对比着看。

概述


总的来说,闭包词法作用域 的体现。你可以通过 这篇文章 的第二节来了解其基本用法。在这篇文章章,我们将通过几个例子,来详细分析闭包。

在阅读完本文章后,你应该能发现,闭包 实际上不是什么新概念,它指的就是函数的 [[scope]] ,即作用域链。**作用域链并不会随着执行上下文从栈从弹出而被销毁。**这就是 闭包 的全部解释了。

接下来,我们通过一个例子来详细分析一下。

Read more
7 种你应该知道的 Javascript 原生错误(翻译)

原文:https://blog.bitsrc.io/types-of-native-errors-in-javascript-you-must-know-b8238d40e492

我们在各处都能看到错误,从浏览器控制台到运行着 Node.js 的设备终端。

这篇博客将主要列举一下我们在 JS 开发中可能邂逅的错误类型。

Tip:更好的使用错误类型,可以让我们从缓慢而地狱般的开发体验中解放出来,享受快速而无痛的编程。当你编写可复用的代码时,请确保你编写的错误清晰而易懂。

Read more
无比芜杂的 JavaScript 执行上下文——变量对象、作用域链和 this

这篇文章是对JavaScript 深入之从 ECMAScript 规范解读 this的一个总结和补充。意在对 JavaScript 执行上下文的来龙去脉做一个基本的梳理。

在开始谈论执行上下文之前,我们必须对 JavaScript 的作用域有一定的了解,才能清楚的理解执行上下文的设计思路。你可以阅读这篇文章的第一部分变量与变量的作用域

执行上下文


关于执行上下文的解释,我们采用 ECMAScript 5.1 规范 的定义。你需要注意的是,在 ES6 开始,对执行上下文的内涵进行了延伸,但它们依旧包含 ES5 的定义。

可执行代码

在讲解执行上下文之前,我们需要了解一下 ES 对于可执行代码的定义。ECMAScript 的可执行代码有三种类型:Global codeEval codeFunction code。你可以通过 ECMAScript 5.1 规范 来详细了解可执行代码。你需要注意的是规范中关于 Function code 的这句话:

The function code of a paticular FunctionBody does not include any source text that is parsed as part of a nested FunctionBody

简单来说,就是一个函数和函数内的嵌套函数,并不属于同一个函数。

Read more
宝刀般的抽象基类——从协议说起

鸭子类型


在 Python 中,创建功能完善的序列并不需要使用继承,而只需要实现符合序列协议的方法。

Python 中的协议是一种动态的,非正式的协议。它仅仅在文档中定义,因此也不能像真正的接口一样,对代码作出限制。这种约定可能会让使用惯了 Java 或是 C# 的程序员感到新奇。

Python 会尽可能的支持基本协议。例如如果你在类中实现了 __getitem__ 方法,那么解释器就会将其当做一个序列来执行,尽管序列协议要求实现的是 __iter__ 方法。

另外的,一个类也可以实现多个接口以扮演不同的角色。

进一步的,你甚至可以在运行时使用 猴子补丁 来事先协议。例如,你可以在运行时为一个类增加 __setitem__ 方法,从而可以使用 random.shuffle 函数打乱序列:

1
2
3
4
5
def set_card(instance, position, card):
instance.__cards[position] = card

>> FrenchDeck.__setitem__ = set_card
>> shuffle(deck)

总的来说,这也就是 鸭子类型 的代表特征——动态协议。

Read more
4个编写高质量 JavaScript 模块的最佳实践(翻译)

原文:https://dmitripavlutin.com/javascript-modules-best-practices/

借助 ES2015 的模块化方案,你可以将应用拆分为可复用的,封装的,仅专注于单一功能的模块。

这确实是个好想法,但随之而来的问题是,你究竟该如何组织这些模块呢?一个模块中应该包含多少个类或函数呢?

这篇文章展示了 4 种如何更好地组织你的 JavaScript 模块的最佳实践。

Read more
构建大厦的基础——模块化

概述


模块化是构建大型应用的基础手段,它可以让代码变得更加易读,易于调试。很多框架作者鼓励用户编写插件来扩展框架功能,这些工作所依赖的,就是一个语言的模块化功能。

相较于 Python 清晰的模块化方案,JavaScript 则显得较为混乱。这主要是由于 JavaScript 早期“浏览器脚本”的定位导致的。社区等不及官方的解决方案,所以自己折腾了几套模块化方案。不过这个问题在 ES6 版本已经改善了很多。

JavaScript

在 ES6 之前,JS 社区为了解决这个问题,提出了很多的模块加载方案。主要有 CommonJSAMD 两种,前者使用与服务器,后者则用于浏览器。另外的,这两种模块都是 运行时加载 的,是非静态的。例如,一个典型的 CommonJS 标准的模块引入是这样写的:

1
2
3
4
5
6
let { hello, goodbye } = require("base");

// 等同于
let _base = require("base");
let hello = _base.hello;
let goodbye = _base.goodbye;

从运行的逻辑上来说,这实际上是先整体加载了一个 base 模块,生成一个对象,然后再从这个对象上读取 2 个方法,赋值给 hellogoodbye

这样做其实就是先把所有东西都拿过来,再挑出来有用的。与之相对的,ES6 提出的模块实现则是静态的。也就是说,你可以按需加载。一个典型的 ES6 加载是这样写的:

1
import { hello, goodbye } from "base";
Read more
从生成器进化为协程——以列表推导为引子

从列表推导开始


Python 有一种被称为 列表推导 的特性,它可以用来快速的构建列表(list)。下面是一个简单的例子:

1
2
l = [1, 2, 3]
[str(i) for i in l] # ['1', '2', '3']

这个概念叫做推导式。它的作用是以一个数据序列为基础,来构建另一个新的数据序列。

在 Python 中,除了列表推导,你还可以使用 字典(dict)推导集合(set)推导 。下面是一些示例:

1
2
3
4
5
6
7
names = ['Dutch', 'Hosea', 'Arthur', 'John']
suits = ['Spade', 'Heart', 'Diamond', 'Club']

[(name, suit) for name in names for suit in suits]

[(name, suit) for name, suit in zip(names, suits)]
# [('Dutch', 'Spade'), ('Hosea', 'Heart'), ('Arthur', 'Diamond'), ('John', 'Club')]

第一种列表推导,会使用 names 中的每一个元素去依次和 suits 中的每一个元素相对应。因为产出元素过多,所以就不详细写出了。

第二种列表推导使用了 zip 函数。这个函数接收可迭代对象为参数,将对象中对应的元素打包成元组。如果你期望从多个数据序列中一一对应的构造新数据序列时,你将会经常的用到这个函数。

1
2
[(name, suit) for name in names for suit in suits if name == 'Arthur']
# [('Arthur', 'Spade'), ('Arthur', 'Heart'), ('Arthur', 'Diamond'), ('Arthur', 'Club')]

上面这个例子是在列表推导中使用了 if 语句。这样可以在构建列表前根据需要,剔除部分数据。

你也可以将这个例子改写为使用高阶函数来实现,但可读性绝壁比不上列表推导。

Read more
闭包与装饰器——从变量的作用域说起

变量与变量的作用域


计算机语言中的变量(Variable)这一概念来源于数学,它是一个存储地址的映射。这个存储地址中通常包含了一段数据或一些抽象信息。你可以使用变量名来方便的引用存储值。

提到变量,就不得不提到 作用域 这个概念了。

实际上,作用域指的是一段计算机程序。在这段程序代码中,变量名与存储地址的映射关系一直保持有效。换句话说,作用域决定了当前程序是否能够访问到某个变量的值。

作用域的解析规则在不同的编程语言中都可能不相同。

作用域分为 静态作用域动态作用域 。前者也叫做 词法作用域 。下面分析一下这两种作用域的区别。

Read more
当我们谈跨域的时候,我们谈些什么

关于域名和请求

客户端在加载资源时,会从服务端请求资源。而具体请求的是哪个资源,则是由域名 指示的。

域名

一个域名主要是由三部分组成的,即——协议,域名,端口号(这三者合起来被称为域)。这三者将指示出一个唯一的服务器主机。换句话说,如果协议,域名,端口号一致,那么我们称其为同域

关于跨域请求

其定义是:当前发起请求的域与该请求指向的资源所在的域不一样。
浏览器基于安全问题,会限制这种请求。具体一点来说,请求会被发送至服务器,而服务器也会做出回应,但浏览器会拦截这次回应。

关于跨域请求的安全问题

CSRF 攻击

CSRF(Cross-site request forgery),即跨站请求伪造。
如果没有跨域限制,当你登录了 A 网站,本地将会存储一个 A 网站相关的 Cookie,而浏览器也会维护这个 Session 会话。如果你在没有登出的时候,就访问了 B 网站,B 网站则可以通过这个 Cookie 来访问 A 网站的资源,从而造成信息泄露或是财产损失。、
简单来说,服务器仅识别身份验证信息,在上面的例子里,就是 Cookie。如果不将网站和服务器限制为同源的,那么任意网站都将可以使用这个身份验证信息来获取服务器的资源。从服务器的角度看过去,若没有做特殊限制,所有具有正确的身份验证信息的请求都是合法的。
你可以通过[这篇文章]详细了解 CSRF 攻击。

Read more