现代JavaScript教程学习笔记
这是我学习现代JavaScript教程的笔记,他们的内容确实好,有一种相见恨晚的感觉,当时要是初学js能遇到就好了。。。 没有抄袭,只是笔记,并尝试自己稍微讲一下,如果涉及侵权,会立刻删除
基础部分
use strict
'use strict'
//现代模式
可以放在脚本开头或者函数体内部
交互
alert 显示信息
alert('111')
prompt
const m = prompt('your name?', 'george')
第二个参数为默认值, m为用户输入值,若esc取消则返回null
confirm
let a = confirm('yes?')
确定为true, 否则为false
??
??与||类似 我们认为null/undefined为未定义,??返回第一个已经定义的值
let a = null ?? 'a'
console.log(a)
数据类型
js共有七种原始类型和一种引用类型 原始类型:
- Number
- BigInt(32n, n结尾代表bigint)
- Bool
- String
- null
- undefined
- Symbol 引用类型
- object
typeof检测类型
console.log(typeof '111')//'string'
注意
console.log(typeof null)//object
console.log(typeof function() {})//function
Null并非object,只是遗留问题; function也是object, 但是typeof可以细致区分
类型转换
js可以将变量类型进行转换,其中包括显示和隐式
显示转换
Number('123') //123
Boolean(1) //true
隐式转换
如果你自己不体面,js引擎就帮你体面
+'123' //number: 123
'6' / '2' //number: 3
虽然是字符串,但是会变为number
js中还有很多奇奇怪怪的转换,但我觉得没人会在实际中搞那种事情吧
代码质量
调试
没啥说的,这原文写的不好总结啊 zh.javascript.info/debugging-c…
代码风格
- 换行
if (true) console.log('***')
if (true) {
console.log('***')
}
相对推荐的两种方式,要么放{}, 要么单行不换行
- 一行不能过长
let a = `hello thank you, thank you very much`
不如
let a = `hello thank you,
thank you very much
`
google JavaScript代码风格:google.github.io/styleguide/…
注释
好的代码不需要注释说明他做了什么,避免
//这段代码先买了瓶水,又喝了口水
推荐 jsDoc:
/**
* 返回x 的 二次方
*
*
* @param {number} x 输入的值
* @return {number} x的2次方
*/
function(x) {
return x ** 2
}
自动化测试
zh.javascript.info/testing-moc…
polyfill和转译器
js不断发展,但是旧的引擎没有内置新的语法,所以我们可以:
- 转译器: 将一种源码转换为另一种源码
let a = b ?? 'a'
转为
let a = (b != undefined && b !== null) ? b : a
- polyfill 则是原来不具备的东西,把它加进去 假设pow函数不具备
if (!Math.pow) {
Math.pow = (x, n) => {
return x ** n
}
}
object
object基本内容
object类型是引用类型,我们可以这么构建一个对象
let obj = {
name: 'george'
}
或者
let obj = new Object()
对于对象,它是由键值对形成的,所以操纵这些键尤为重要
delete obj.name //删除
obj.age = 22//添加
obj.name = 'a'//修改
'name' in obj //判断是否存在
for (let k in obj) {
console.log(obj[k])
}
//遍历
操纵key可以通过点号.和中括号[]
obj.name
obj['name']
let k = 3
obj[k]//中括号可以放变量
js中的key都是string类型,无论你怎么搞(Symbol类型除外) key中整数类型的key按大小排序,其他按创建顺序排序
let obj = {
45: 1,
2: 2
}
for (let i in obj) {
console.log(obj[i])
}
let obj = {
name: '2',
age:'3'
}
for (let i in obj) {
console.log(obj[i])
}
克隆
因为对象是引用类型
let obj = {
name: '1'
}
obj更像是一个名片,它指向一个内存,上面记录着{name: '1'},大概这个感觉吧,其实学过c可能会比较理解一点
所以,有如下:
let user = {}
let a = {}
a === user//false
let user = {}
let b = user
user === b//true
比较的是是否来自一个引用
那么如何克隆一个对象?
let user = {
name: '11'
}
let a = Object.assign({}, user)
console.log(a)
最后a为克隆对象,第一个参数也变了,如果键有重复,会出现覆盖现象
如果出现某个属性值为对象,则需要深度克隆, Object.assign()将不起作用
let user = {
name: '11',
a: {
name: '2'
},
b: [2,3,5]
}
let a = {}
deepClone(user, a)
console.log(a)
function deepClone(obj, temp) {
for (let i in obj) {
if (typeof i === 'object') {
temp[i] = {}
deepClone(obj[i], temp[i])
} else if(typeof i === 'array'){
temp[i] = []
deepClone(obj[i], temp[i])
} else {
temp[i] = obj[i]
}
}
}
大概这样,依赖递归,这是个大体思路
?.可选链
在代码中,会有这样的问题
let a = user.friends.name
如果name不存在, user.friends为undefined,则会报错不能访问undefined的name 我们可以
if (user.friends) {
a = user.friends.name
}
但如果一个更复杂的属性呢? 可选链?.帮我们解决了很多
let a = user?.friends
如果user存在返回user.friends
不存在返回undefined
这只对前面的user, 对后面不起到检查作用, 可以在这些情况下使用它
let user = {
a: {
name: 'a'
},
b() {
console.log('ccc')
},
hi: 3
}
user?.a?.name
user.b?.()//调用函数
user?.['hi']
但如果user是个必须存在的值,不建议user后面加?.否则,如果user那里出现问题,我们将无从知道
this
js是动态的语言,所以它是相对自由的,所以this也很自由,所以就出现各种烦人的this问题
let user = {
name: '111',
fun() {
console.log(this.name)
}
}
user.fun()//111
看样子如同预期,user.fun()打印了user的name
但是
function fun () {
console.log(this.name)
}
let user = {
name: 'user'
}
let obj = {
name: 'obj'
}
user.f = fun
obj.f = fun
user.f()//user
obj.f()//obj
可以发现js中的this指向调用者无论是.还是[]
特例:箭头函数没有this,所以箭头函数中的this为它外部函数this指向
let user = {
firstName: "Ilya",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // Ilya
Symbol
symbol类型可以如此创建
let id = Symbol('id')
第一次学感觉没啥用,后来接触的多点渐渐体会到它的用处了 symbol是唯一的,括号中是该symbol的描述,哪怕描述一样,两个symbol也不等,且symbol不能自动转换为字符串,需要显示转换
let id = Symbol('id')
let id1 = Symbol('id')
console.log(id === id1)//false
全局注册表
let sy = Symbol.for('name')//如果存在描述为name的symbol就返回,否则就创建
let m = Symbol.keyFor(sy) //返回name
object可以用symbol做键,这样在for...in, Object.keys()等不会访问到,想当与一定程度的隐身;Objcet.assign()则可以获取到 当然,symbol在内置系统中用到很多,如对象是否可迭代,需要判断是否有一个symbol属性,等等
垃圾回收
垃圾回收会回收掉不再可达的值,例如
let user = {}
user = null
那么user指向的对象就被回收了
let a = {}
let admin = {}
admin = a
a = null//不会清除,因为还有admin引用
垃圾清除会找到所有根并标记,然后标记他们的引用,然后再标记引用的引用,最后没标记的清除
构造器和new
函数用new,就是构造函数
function User(name) {
this.name = name
}
let user = new User('aa')
console.log(user)
这其中发生了什么
function User(name) {
//this = {} 创建空对象,并设置this
this.name = name //添加属性到this
//return this返回this
}
new.target判断是否用了new
function User(name) {
if (!new.target) {
return new User(name)
}
this.name = name
}
new User('aa')
如果构造器return对象,则接受这个对象,覆盖原有this 否则返回this
object 转换为原始类型
alert(obj)//obj-> string
+obj //obj -> number
类型转换有三种辩题,发生在string、number、default三种情况,成为hint string hint:
alert(obj)
aObj[obj] = 123 // obj做键
number hint:
let num = Number(obj)
let n = +obj
let delta = date1 - date2
let greater = user1 > user2
default hint:
let total = obj1 + obj2
if (user == 1) {}
根据情况,哪种情况用哪种hint, 不明确一般就是default hint
为了转换,调用objSymbol.toPrimitive--带有symbol键的Symbol.toPrimitive的方法,如果存在 否则,如果hint是string--尝试obj.toString()和obj.valueOf(),无论哪个存在 否则,如果hint是number或default--尝试obj.valueOf()和obj.toString(),无论哪个存在
Symbol.toPrimitive
let obj = {
name: 'a'
}
obj[Symbol.toPrimitive] = function(hint) {
console.log(hint)
return hint === 'string' ? 'this.name' : 'e'
}
console.log(+obj) //number NaN
console.log(obj + 500) //number e500
toString valueOf
对于string hint, 存在toString就用, 不然用valueOf 对于其他hint, valueOf不存在用toString, 否则用valueOf 普通对象拥有这两个方法,toString返回'[object Object]',valueOf返回对象自身
let user = {
name: 'a',
money: 100,
toString() {
return `${this.name}`
},
valueOf() {
return this.money
}
}
alert(user)
alert(+user)
他们可以这样用
转换可以返回任意原始类型
这是我用vue3实现的饿了么,如果您感兴趣,希望您能看看,谢谢 github.com/goddessIU/v…
项目预览地址 goddessiu.github.io/