《你不知道的JavaScript(上卷)》阅读随笔

早期看书时随笔写下的一些事物,🐟仅供娱乐,切勿认真。

读后感

  • 其内容并不怎么需要 ES6,还会说明 var 存在的一些问题。
  • 非常好的说明了作用域闭包的存在,当然真实的情况是,初期容易被闭包给吓住,也不知道为什么要通过作用域保护一个变量。
  • 质量很高,但是初期不要看,因为看了或许会用不着,而作用域是例外,因为一直都在使用。

作用域与闭包

作用域

var a = 1 这条语句,看似简单的背后,实则分为两步:

  1. 先看一半,var a,查看当前作用域下是否存在 a 这个变量,无则声明,有则替换。
  2. 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 开发者是很可怕的,可能放在当前来说,可以逃避这个机制的存在,毕竟现在的风向是在向函数式编程靠拢。但是不好意思,你不合格,甚至在 hooksvue3 到来之前,那是撑不下去的。

绑定规则

默认绑定

一个函数执行时,在非严格模式下,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 让你不得不懂 thisvue2 让你不能用 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() 调用了那一个函数罢了

虽然种种不对劲,但该用还得用,谁不愿意吃这颗糖呢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值