JS浅入学习笔记

本文最后更新于:2 个月前

特性、函数和对象

语言特性

引用与值

js 中有两种方式对变量进行保存:值和引用

对于原始值在对其他变量赋值的时候都是复制到变量中,原始值有以下几样:

  • string
  • number
  • boolean
  • null
  • undefined

其最重要的特点是:他们是之间按照值进行赋值,赋值,传递函数参数以及返回函数结果

而其他的数据类型则是通过引用的方式,没有复制原始值而是保存对象的引用,这样子做是为了提高操作的效率,一次修改就能反映到其他的引用中

1
2
3
4
5
6
7
var obj = {}

var refObj = obj

obj.p = true

console.log(obj.p === refObj.p) // true

作用域

在 JS 中只用两种作用域:全局作用域与函数作用域

即函数内是一个全新的作用域,其他地方均为全局作用域

1
2
3
4
5
6
7
8
9
10
{
var a = 0
}

(()=>{
var a = 1
console.log(a) // 1
})()

console.log(a) // 0

不过在 ES6 中推出了新的 let 这一声明变量的方法,使得变量的作用域范围可以被限制在一个块级内:

1
2
3
4
5
{
let a = 0
}

console.log(a) // 报错:a 未定义

因此在各种 for 循环或者 while 循环中都推荐使用 let 对变量进行声明,控制其作用域不会污染全局

隐式全局变量声明

1
2
3
4
5
6
// 全局环境下
function test() {
foo = 'bar'
}

console.log(window.foo) // 'bar'

这段代码中是 window 对象调用了 test,所以函数中 this 指向 window。而 foo 前面又没有关键字 var 所以这个变量值被放入了全局中

在使用 var 的时候 js 会把用 var 标记的变量先全部提升到顶部,从而保证全局变量可用

上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 浏览器环境
function setFoo(fooInput) {
this.foo = fooInput
}

var foo = 5
var obj = {
foo: 10
}

setFoo(15) // window 调用,赋值到 window 上

obj.setFoo = setFoo
obj.setFoo(20)

console.log(foo, obj.foo) // 15 20

这段代码在 浏览器环境 下与 Nodejs环境 下运行结果并不相同,在 nodejs 中 第一个 foo = 5 是被定义到 Local 环境中,而在浏览器则是定义在 window 中;nodejs 下 第一个 setFoo 指向的环境是 Global 而不是 Local 所以 nodejs 输出的结果是 5 20 ,而浏览器是 15 20

使用函数的 call()、apply() 和 bind() 改变上下文

他们都是用来为函数指定上下文(即 this 指向):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var name = '小王', age = 17
var obj = {
name: '小张',
objAge: this.age,
myFun: function (f,t) {
console.log(this.name, this.age, 'from:'+f+' to:'+t)
}
}

var db = {
name: '小红',
age: 24
}

obj.myFun.call(db, '成都', '上海') // 直接用逗号分隔传入的参数
obj.myFun.apply(db, ['成都', '上海']) // 用一个数组将所有参数包起来
obj.myFun.bind(db, '成都', '上海')() // 与 call 同理,不过返回一个函数,需要手动调用,不会直接执行

闭包

一般用于限制变量的作用域

下面这里第一段代码会在控制台循环打印出 5 个 5,因为 js 是单线程任务,在这个例子中 当开始执行 setTimeout 的回调时全局的 i 的值已经是 5 了,所以会循环打印出 5 个 5,对此我们可以使用闭包,即封装一个立即执行函数,限制变量的作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for (var i = 0; i < 5; i++) {

// 未使用闭包
setTimeout(()=>{
console.log(i)
},i*1000)

// 使用了闭包
(function (j) {
setTimeout(()=>{
console.log(j)
},j*1000)
})(i)

}

下面这段代码通过闭包将全局变量进行了隐藏

1
2
3
4
5
6
7
8
(function () {
var msg = 'msg'

window.onload = function () {
// 使用隐藏的变量,避免这个内部的变量影响到外部的变量
console.log('msg')
}
})()

函数重载

js 本质上来说其实并没有函数重载,而 typescript 也仅仅只是提供了函数声明的重载。

但是我们可以利用函数自身作用域下的 arguments 来自己模拟重载

1
2
3
4
5
6
7
8
9
10
function f1(t2, t3) {
if (arguments.length == 2) {
// 传入了两个变量,将变量转为数组
var arr = Array.prototype.slice.call(arguments, 0)
console.log(arr)
}
else {
// 传入了其他数量的变量
}
}

typeof 与 instanceof

他们都可以用来判断某个变量的类型,但是 typeof 只能判断原始值,对于一些复杂结构如 Object, Array 都是返回 "object"

因此便有了 instanceof 关键字,语法:object instanceof constructor

用来检测 constructor.prototype 是否存在于参数 object 的原型链上。

1
2
3
4
5
const arr = []
console.log(typeof arr) // object
console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true,因为 Array 的构造函数的原型链上存在着 Object 构造函数的 prototype 对象
console.log(arr instanceof Date) // false,Date 的构造函数原型是 Date.prototype {},他不存在于 Array 上

对象工具

直接说点高级技巧

修改对象

阻止对象写入新的属性

1
2
3
4
var obj = {}

// 冻结对象方法
var obj2 = Object.preventExtensions(obj)

此时往对象内新增值时会抛出错误,但是可以更新与删除

阻止对象的扩展能力

1
2
3
var obj = {}

Object.seal(obj)

这种方法会使得对象属性无法添加和删除,也不能转换为存取器 (即定义 getter 和 setter),但是可以对对象的属性值进行更新

控制对象变得完全不可变

1
2
3
var obj = {}

Object.freeze(obj)

此时对象已经变得完全不可变了,除非其属性中有以对象为值的属性,那么这个属性仍然可以读写,必须通过递归的形式进行深度冻结才可以使得所有属性被冻结

创建可重用代码

面向对象?

直接上一些高级技巧

isPrototypeOf() & getPrototypeOf()

isPrototypeOf() 这个方法可以在任何对象上进行调用,其存在于所有 JS 对象中,其用于判断 一个对象是否是某种类型的实例

1
2
3
4
5
6
7
8
9
10
11
class Person {
constructor() {
this.name = 'foo'
}
}

let obj = Object.create(Person)
let obj2 = new Person()

console.log(Person.isPrototypeOf(obj)) // true
console.log(Person.isPrototypeOf(obj2)) // false,因为 new 出来的属性没有其标准的 prototype 属性

通过函数闭包控制属性成员私有化

1
2
3
4
5
6
7
8
9
10
11
12
class Person {
constructor() {
this.name = 'foo'
var privateN = 10

this.getPrivate = () => privateN
}
}
const obj = new Person()

var test = obj.getPrivate()
console.log(obj.test); // 10

这种方法只能对原始值进行处理,如果是复杂数据结构可能要考虑使用 Map 或者 WeakMap 进行处理


JS浅入学习笔记
https://blog.shiinafan.top/2022/11/02/JS浅入学习笔记/
作者
Shiina
发布于
2022年11月2日
许可协议