早期看书时随笔写下的一些事物,🐟仅供娱乐,切勿认真。
读后感
- 其内容并不怎么需要
ES6
,还会说明var
存在的一些问题。 - 非常好的说明了作用域与闭包的存在,当然真实的情况是,初期容易被闭包给吓住,也不知道为什么要通过作用域保护一个变量。
- 质量很高,但是初期不要看,因为看了或许会用不着,而作用域是例外,因为一直都在使用。
作用域与闭包
作用域
var a = 1
这条语句,看似简单的背后,实则分为两步:
- 先看一半,
var a
,查看当前作用域下是否存在a
这个变量,无则声明,有则替换。 a = 10
,查找变量a
的存在,找到则赋值,没有那也没事,就当作window
的属性。
这是什么?既可以重复声明,又不需要声明,闹着玩呢。
这是一个建立在全局作用域下的错误,切记,不可在全局作用域下进行这些花里胡哨的操作,你需要拿一个函数当外壳,利用函数的作用域来避免此类事件的发生。
作用域的存在是必要的,要是所有的变量存在同一片天地,那才是要翻天了,同时,使用作用域来隐藏一些数据与变量,遵从最小授权和最小暴露原则。
欺诈作用域
有几种欺骗作用域的玩法,当然这是不会推荐使用的,利用他们的特性可以动态生成变量,让人防不胜防,不过平时做点计算啥的确实要方便的多。
eval()
setTimeout()
setInterval()
new Function()
with
,被咔嚓掉的方法:
const obj = {
a: 10,
b: 20
}
with(obj){
console.log(a)// 10
console.log(b)// 20
}
当然从现在看来,使用结构赋值完全可以代替它,就不必怀恋了。
IIFE
区分函数声明和表达式最简单的方法是看
function
关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。如果function
是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。
(function foo(){ ... })()
第一个 ( ) 将函数变成表达式,第二个 ( ) 执行了这个函数。这种模式很常见,几年前社区给它规定了一个术语:IIFE
,立即执行函数表达式(Immediately Invoked Function Expression)
IIFE
一个应用场景是解决 undefined 标识符的默认值被错误覆盖导致的异常(虽然不常见)。将一个参数命名为 undefined,但是在对应的位置不传入任何值,这样就可以保证在代码块中 undefined 标识符的值真的是 undefined。这种用法在 jQuery
当中可见
没错,IE 问题
此外,除了双括号法,另一种寻常的写法是在首位添加 !
,不过不能用于箭头函数
!function(){
console.log("我执行了")
}()
变量提升
var
存在变量提升现象,但是比不过函数声明的提升,也不会耽误后面的赋值操作,瞧,后一句不就输出 2 了
fn()// 函数执行 1
function fn() {
console.log(1)
}
var fn = 2
console.log(fn)// 2
如果还有一个函数存在在两者的后头,那么它会上位,将前一个函数声明给顶掉
闭包
既非风动,亦非幡动,仁者心动耳
高端大气上档次,还易学,你说气人不,给我上图
function fn() {
let num = 0
const getNum = () => num
const add = () => num++
return {
getNum,
add
}
}
一段意外的输出,给出答案 666666
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
因为延迟存在,等到输出的时候,i 已经升级到 6 了,这很合理。对此,可以使用 IIFE
来解决这个问题
for (var i = 1; i <= 5; i++) {
(function(i) {
setTimeout(function timer() {
console.log(i)
}, i * 100)
})(i)
}
而 let
不存在此问题,因为它拥有块级作用域,啊,什么是快乐来着?
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
模块化
在 ES6
到来之前,想要实现模块化,则只有闭包这条路,可是为什么 jQuery
它没有这么玩,作者是不可能不懂闭包的使用法则的。
我自己推敲而动,因为这样会增加使用成本,当前我只需要加载一个文件就好了,而如果转换为闭包模块化,那么就需要多下载几个文件
不好意思重来,因为没办法使用,这需要用户手动,按顺序导入文件,不能出纰漏,所以何必吃这个苦头,再说在当时的好处也看不着,在开发环境是不错,甚至可以说,开发时我自己按这规矩来,发布就粘贴到一块,供人方便使用。
不得不感叹有点慢,毕竟 sass 和 css 似乎都可以通过 @import
引入其他同类型文件,虽然不知道他们多久出的。
这同时也解释了,在 HTML
当中,想使用 import
,就需要将 script
标签的 type
改成 module
。那不然,就可以破除作用域的存在,直捣黄龙。
this
任何足够先进的技术都和魔法无异
没想到啊,我又回来了,看到你小子就觉得不太妙。
不懂 this
的 JavaScript 开发者是很可怕的,可能放在当前来说,可以逃避这个机制的存在,毕竟现在的风向是在向函数式编程靠拢。但是不好意思,你不合格,甚至在 hooks
与 vue3
到来之前,那是撑不下去的。
绑定规则
默认绑定
一个函数执行时,在非严格模式下,this
指向的便是 window
正常情况下,我们要免于使用这种情况下的 this
,别犯二使用 this
来代替 window
fn() // true 调用时其实是这样 window.fn()
function fn() {
console.log(this === window)
}
隐式绑定
与前面的不同,当函数是由一个对象调用的,则当中的 this
则会指向那个对象,若是按照中册说法,这一条对我来说,是不是可以成为显式绑定呢
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
另外,别想着借用方法,这可不能这么玩,调用的对象不对
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
const fn = obj.foo
fn() //undefined this => window
显示绑定
当然,其中会提供相应的显示绑定,这一点不用慌,此方法在函数原型上,是个函数就能使用的方法
calll()
apply()
bind()
老熟人了,这里就一笔带过
new 绑定
在 JavaScript
的世界当中,并不存在 class
,这只是一种语法糖罢了,真实的操作还是函数
也就是说,我们之上通过 new
来调用函数,而不存在于什么构造函数,只是说命名特殊一点
当然,通过 new
调用的函数,会改变该函数的 this
指向,变成一个对象,且该函数的返回值将变成 this
,这都是隐式完成的
各绑定的优先级如何
事实就是,从上至下,从小到大
箭头函数
在接触了 ES6
中的箭头函数后,就恨不得把所有的函数改成箭头函数形式。但是有时候会遇到一些莫名其妙的错误,通过去除箭头函数就达到了想要的效果🤣这也是被 this
迎头痛击,不知道是因为 this
所导致的
后来玩的是啥,react
让你不得不懂 this
,vue2
让你不能用 this
,这个时代容不下你👏
对象
对象与 this
是息息相关的,毕竟 this
可不会执行一个基础类型
function
也属于对象,专业说法则是,可调用的对象
string
作为一个基础数据类型,它为什么可以当作对象来使用,下面这些语句就和呼吸般自然
const str = "abc"
str.length
str.slice()
这是在运行时自动进行了一波转换,而无需手动创建对象
创建对象时,可以拼接 key
我应该没有忘,想要做到的是在 TS
当中申明接口时使用这一点,但是那货不让这么玩,迂回拼接都不行,就得明写
const key = "name"
const obj = {
[key]: "a",
age: 10
}
console.log(obj) // { name: "a", age: 10 }
处于 Object
对象上的静态方法,随便写写
没有翻译的日子不好过啊,这些玩意都不知道意思是什么
assign()
:将两个对象合并到一块,浅拷贝·defineProperty()
:配置对象属性,多的就不说了, 跳preventExtensions()
:禁止添加新属性seal()
:不可添加与删除freeze()
:再次升级,啥都干不了,不过这是浅层次的,对象属性就管不了create()
:创建对象,很单纯
而对象实例则可以玩这些 hasOwnProperty()
,用来检测对象身上是否有参数 key
,与 in
操作符相比,此函数不会检查原型,可以确保 key
是否存在与对象上
类
哼哼哼,JavaScript
当中的 class
只是一种语法糖,当然了,面向对象我也觉得烦,是可选的最好,那种起手就是 class
的,也是见了鬼
原型
好家伙,原来还有这么一套在里头
当对象本身没有一些东西时,会去尝试从原型上搜寻,若是存在相应的 key
方法,则会将对应的数据带回,存在多个,就近原则。而下面这一段,当给 temp
添加 a
时,可以触发 obj
当中 setter
方法
const obj = {
value: 10,
get a(){
return this.value
},
set a(newValue){
console.log("触发")
this.value = newValue
return true
}
}
const temp = Object.create(obj)
temp.a = 20
console.log(obj)
console.log(temp)
想要避免该情况,可以使用
defineProperty()
来添加属性
在“继承”前面加上“原型”对于事实的曲解就好像一只手拿橘子一只手拿苹果,然后把苹果叫作“红橘子”一样。无论添加什么标签都无法改变事实:一种水果是苹果,另一种是橘子。
更好的方法是直接把苹果叫作苹果——使用更加准确并且直接的术语。这样有助于理解它们的相似之处以及不同之处,因为我们大家都明白“苹果”的含义
揭破 JavaScript
当中没有继承这一回事,实现这一点的方式是通过原型继承
怪不得现在要手写 new
操作符,这一点确实可以做到,所谓的 class
继承也是扯淡,就是内部使用 call()
调用了那一个函数罢了
虽然种种不对劲,但该用还得用,谁不愿意吃这颗糖呢