tianleyy / tianleyy.github.io Goto Github PK
View Code? Open in Web Editor NEWpersonal page
personal page
在JavaScript学习的过程中,遇见一些很有意思的文章,但是在分类的时候,不知道怎么将其分类(归根到底就是菜)。
现阶段,结合自己的实际情况,先将这些有意思的文章,都归结在这里,看完之后做一下知识小结,记录一下。
如何编写高质量的 JS 函数(1) -- 敲山震虎篇
如何编写高质量的 JS 函数(2) -- 命名/注释/鲁棒篇
如何编写高质量的 JS 函数(3) --函数式编程[理论篇]
如何编写高质量的 JS 函数(4) --函数式编程[实战篇]
JavaScript一般有按值传递和按引用传递两种复制方式
按值传递的是基本数据类型, 一般存放于内存中的栈区,存取速度快,存放量小。
按引用传递的是引用类型,一般存放于内存中的堆区,存取速度慢,存放量大,其引用指针存于栈区,并指向引用本身。
浅拷贝:指两个JS对象指向同一个内存地址,其中一个改变会影响另外一个;
深拷贝:指复制后的新对象指向一个新的内存地址,两个对象改变互不影响。
深拷贝涉及到的范围很广,包括函数的拷贝、类的拷贝、Promise对象的拷贝等;
现在在开发中遇见的拷贝,主要是Json数据对象为主,其他的暂时没有遇见。
const obj = {
name: 'JavaScript',
type: 'FrontEnd'
}
const copyObj = obj // 赋值操作就是一种简单的浅拷贝
Object.assign()方法是ES6的新函数,可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
拷贝的是对象的属性的引用,而不是对象本身。Object.assign()可以实现一层深拷贝。
let obj = {
a: {a: 'hello'},
b: 33
}
let newObj = Object.assign({}, obj)
newObj.a.a = 'Hello JavaScript.'
newObj.b = '一层深拷贝'
console.log(obj, newObj)
浅拷贝是我们经常使用的操作一些对象或数组的有效方法,具体的使用需要结合实际场景来合理使用,同时需要考虑一些兼容性问题(IE的版本是否兼容)。 但是在大多数实际场景中,我们需要使用更多的是深拷贝,尤其是在各种MVVM框架框架中,引入了状态管理,例如vue的store,更多的体现在数据流中希望我们不改变原对象。这种使用就需要通过
深拷贝
的方式去实现。
能够处理JSON格式的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝,并且会直接丢失相应的值。
它还会抛弃对象的constructor
。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。
如果对象中存在循环引用的情况也无法正常处理。
let obj = {
a: {a: 'hello'},
b: 33
}
let copyObj = JSON.parse(JSON.stringify(obj))
console.log(copyObj)
deepClone()
通过递归实现一个简单的深拷贝函数
function deepClone(obj) {
if (typeof obj !== 'object') return // 判断obj是否为object,如果不是则直接return
let newObj = obj instanceof Array ? [] : {}
for(let key in obj) {
if (obj.hasOwnProperty(key)) { // hasOwnProperty 对象自身的属性中是否具有指定的属性
newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
}
}
return newObj
}
const testObj = {
type: 'tianle',
tianel: { name: 'tianle', age: 24 },
laugth: function() {
console.log('哈哈哈哈哈哈')
}
}
const newObj = deepClone(testObj)
console.log(testObj)
console.log(newObj)
Object.assign()
JSON.parse(JSON.stringify())
Loadsh.cloneDeep()
(暂未使用过)通过
concat()
、slice()
、拓展运算符进行的拷贝都是浅拷贝
concat()方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
const arrObj01 = [
{ name: 'java', type: 'BackEnd' }
]
const arrObj02 = [
{ name: 'JavaScript', type: 'FrontEnd' }
]
const arrObj = arrObj01.concat(arrObj02) // concat()方式
arrObj[0].name = 'vue' // 对arrObj的属性作改变,arrObj01的属性也会改变
console.log(arrObj, arrObj01)
const arrObj03 = [...arrObj01, ...arrObj02] // 拓展运算符方式
arrObj03[0].name = 'Node.js'
console.log(arrObj03, arrObj01)
slice()方法返回一个新的数组对象,这一对象是由begin和end决定的原数组的浅拷贝(包括begin, 不包括end),原数组不改变。
const arrObj01 = [
{ name: 'java', type: { demo: 'demo' } }
]
const arrObj02 = [
{ name: 'JavaScript', type: 'FrontEnd' }
]
const arrObj = arrObj01.slice()
arrObj[0].name = 'vue' // 对arrobj的值改变,arrObj01对应的值也会发生改变
console.log(arrObj, arrObj01)
准备使用Issues来记录学习过程中的一些收获,所以熟悉一下Markdown的语法。
Markdown的语法相对比较简单,熟悉语法的写法之后即可快速上手。
在Issues编辑发布的右上角也有常用语法的快捷方式,使用非常便捷。
在一行中使用三个以上 星号、减号、底线 来建立一个分割线,行内不能有其他东西
在文字的两端加上两个 波浪线
删除线
这是一个区块 -- >加空格,再填写内容
在列表中使用区块
4个空格可以让区块缩进
段落上的一个函数或片段的代码 -- 用反引号包起来
console.log
函数
代码区块 -- 三个反引号包裹一段代码并指定一种语言(可以不指定)
``` 包裹一段代码 -- 指定语言后代码会提示高亮
console.log(`name is tianle`)
console.log('Jos is an enginner')
链接的使用方法 -- [链接名称](链接地址)
图片的使用方法 -- ![alt 属性文本](图片地址 "可选标题")
first | second |
---|---|
在Markdown中使用表格感觉非常不方便 | 2 |
在Markdown中使用表格感觉非常不方便 | 2 |
Markdown使用了很多特殊符号表示特殊的意义
如果需要显示特定的符号需要使用转义字符
Markdown 使用反斜杠转义特殊字符 \
JavaScript从定义到执行,JS引擎在实现层做了很多初始化工作。
在学习JS引擎工作机制之前,需要引入几个相关的概念,这些组件是JS引擎工作的核心组件。
我们将从全局初始化、执行函数A,执行函数B三个阶段来分析JS引擎的工作机制
var x = 1 // 定义全局变量 x
function A(y) {
var x = 2 // 定义一个局部变量 x
function B(z) { // 定义一个内部函数 B
console.log('function B -- ', x + y + z)
}
return B // 返回函数B的引用
}
var C = A(1) // 执行函数A,返回B
C(1) // 执行函数B,输出4
JS引擎在进入一段可执行的代码时,需要完成以下三个初始化工作
全局对象只存在一份,它的属性在任何地方都可以访问,它的存在伴随着应用程序的整个生命周期。
全局对象在创建时,将Math, String, Date, document等常用的JS对象作为其属性。
全局对象不能通过名字直接访问,因此使用属性window,并将window指向自身,就可以通过window访问这个全局对象了。
// 伪代码 -- 创建全局对象
var globalObject = {
Math: {},
String: {},
Date: {},
document: {}, // DOM操作
window: this // 让window属性指向自身
... // 其他的属性
}
执行环境栈的作用是保证程序能够按照正确的顺序被执行(栈的特性为后进先出)
在JavaScript中,每个函数都有自己的执行环境。
当执行一个函数时,该函数的执行环境就会被推入执行环境栈的顶部并获取执行权。
当这个函数执行完毕,它的执行环境又从这个栈的顶部删除,并把执行权还给之前的执行环境。
// 伪代码 -- 模拟执行环境栈和全局执行环境(EC)的关系
var ECStack = [] // 定义一个执行环境栈,类似于数组
var EC = {} // 创建一个执行空间 --> 在内存中分配一块空间
ECStack.push(EC) // 进入函数 -> 从执行栈的尾部添加EC的执行空间
ECStack.pop(EC) // 函数返回后 -> 从执行栈的尾部删除EC的执行空间
创建一个与全局执行环境(EC)关联的全局变量对象(VO), 并把VO指向全局对象.
每个函数在定义的时候,都会创建一个与之关联的 scope 属性,scope 总是指向定义函数时所在的环境。
全局变量对象(VO)包含全局对象的原有属性,以及全局定义的变量 x 和函数 A
在定义函数 A 的时候,为 A 添加一个内部属性 scope, 并将 scope 指向 VO
// ECStack 结构 -- 执行环境栈
ECStack = [ // 执行环境栈
EC(G) = { // 全局执行环境
VO(G): { // 定义全局变量对象
... // 包含全局对象原有的属性
x = 1 // 定义变量
A = function() {} // 定义函数A
A[[scope]] = this // 定义A的scope,并赋值给VO本身
}
}
]
执行进入A(1)时,JS引擎需要完成以下工作:
JS引擎会创建函数A的执行环境EC(A),然后EC(A)推入执行环境栈的顶部并获取执行权。
在JS中,每个执行环境都有自己的作用域链,用于标识符解析。
执行环境被创建时,它的作用域链就初始化为当前运行函数 scope 所包含的对象。
JS引擎会创建一个当前函数的活动对象(AO) => 活动对象扮演着变量对象的角色
// 运行函数A时的执行环境栈
ECStack = [
EC(A) = { // A的执行环境
[scope]: VO(G), // 全局变量对象
AO(A): { // 创建函数A的活动对象 => 函数A的变量对象
y: 1,
x: 2, // 定义局部变量x
B: function () { ... }, // 定义函数B
B[[scope]] = this, // this值AO(A)本身,而AO(A)位于作用域链顶端,因此B[[scope]]指向整个作用域链
arguments: [], // 我们在函数中访问的 arguments 就是AO中的 arguments
this: window // 函数中的this指向调用者window对象
},
// 链表初始化为A[[scope]], 然后再把AO加入作用域顶端,此时A的作用域链为 AO(A) -> VO(G)
scopeChain: <AO(A), A[[scope]]>
},
EC(G) = { // 全局执行环境
VO(G): { // 创建全局变量对象
... // 包含全局对象原有的属性
x = 1, // 定义变量x
A = function() { ... }, // 定义函数A
A[[scope]] = this // 定义A的scope, A[[scope]] == VO(G)
}
}
]
函数A被执行之后,返回函数B的引用,并赋值给变量C, 执行C(1) 就相当于执行 B(1).
JS引擎需要完成如下工作:
创建函数B的执行环境EC(B), 然后将EC(B)推入执行环境栈的顶部并获取执行权。
创建函数B的作用域链,并初始化为函数B的 scope 所包含的对象,即包含了A的作用域链。
创建函数B的活动对象AO(B),并将B的形参 z, arguments对象 和 this对象作用AO(B)的属性。
当函数B执行"x+y+z"时,需要对x, y, z三个标识符进行一一解析
解析过程遵循变量查找规则:
函数B的作用域链 AO(B) -> AO(A) -> VO(G)
因此变量 x 会在 AO(A) 中被找到,而不会查找VO(G)中的 x,变量 y 也会在AO(A)中被找到,z 在自身的AO(B)中找到。
所以执行结果为 2+1+1 = 4
// 运行函数B时的执行环境栈
ECStack = [ // 执行环境栈
EC(B) = { // 创建函数B的执行环境, 并位于作用域的顶端
[scope]: AO(A), // 指向函数A的作用域链, AO(A) -> VO(G)
AO(B) = { // 创建函数B的活动对象
z: 1,
arguments: [],
this: window
},
// 链表初始化为B[[scope]], 再将AO(B)加入链表表头,此时B的作用域链:AO(B) -> AO(A) -> VO(G)
scopeChain: <AO(B), B[[scope]]>
},
EC(A), // A的执行环境已经从栈顶被删除
EC(G) = { // 全局执行环境
VO(G): { // 创建全局变量对象
... // 包含全局对象原有的属性
x = 1, // 定义变量x
A = function() { ... }, // 定义函数A
A[[scope]] = this // 定义A的scope, A[[scope]] == VO(G)
}
}
]
了解JS引擎的工作机制之后,不能只停留在概念理解的层面,而要将其作为基础工具,用以优化和改善我们在实际工作中的代码,提高执行效率。产生实际价值才是我们真正的目的。
就如变量查找机制,如果代码嵌套很深,每引用一次全局变量,JS引擎就需要查找整个作用域链。
比如处于作用域最底端的window和document对象就存在整个问题,围绕这个问题可以做很多性能优化的工作。
几乎将这篇文章都抄下来了。对于JS引擎的工作原理,还是懵懵懂懂。看完之后,能了解一点点,JS真正的性能优化的一些原理,而不仅仅是图片压缩以所见请求时间之类的优化。
Originally posted by @Tianleyy in #5 (comment)
不需要手动进行换算
通过配置控制不想进行转换的属性或类选择器
css代码中,只需要使用一种单位 px 即可
npm install postcss-px-to-viewport
module.exports = {
plugins: {
autoprefixer: {}, // 给不同的浏览器自动添加相应前缀,如 -webkit-, -moz-等等
"postcss-px-to-viewport": {
unitToConvert: 'px', // 需要进行转化的单位
viewportWidth: 375, // UI设计稿的宽度
viewportHeight: 667, // 视口的高度,对应的是设计稿的高度(也可以不配置)
unitPrecision: 5, // 转换后的精度,即小数点位数
propList: ['*'], // 能转化为 vw 的属性列表
viewportUnit: 'vw', // 指定需要转换成的视口单位,建议使用vw
fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位,默认vw
selectorBlackList: ['ignore', 'tab-bar'], // 转换的黑名单,在黑名单里写入字符串,只要类名包含这个字符串,就不会被匹配
minPixelValue: 1, // 默认为1,小于或等于1px则不进行转换
mediaQuery: false, // 是否在媒体查询的css代码中也进行转换,默认 false
replace: true, // 是否转换后直接更换属性值
exclude:[/Tabbar/], // 设置忽略文件,用正则做目录名匹配
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media(orientation: landscape)
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 1134 // 横屏时使用的视口宽度,同设计稿对应即可
}
}
}
修改了项目的配置文件之后,必须重新运行项目,使新的配置项生效。
移动端项目一般都会使用到UI框架,像我们之前使用到 vant,vant是根据 375px 的设计稿去实现的。所以建议将 viewportWidth 设置 375 即可。
propList
有一些属性的单位,我们不希望转换的时候,可以添加在数组后面,并在前面加上 ! 号
propList: ['*', '!letter-spacing'] // 表示所有css属性的属性单位都进行转化,但 letter-spacing 除外
selectorBlackList
转换的黑名单,在黑名单中添加字符串,只要类名包含该字符串,就不会被匹配。
selectorBlackList: ['wrap'] // 表示如 wrap, my-wrap, wrapper 这样的类名单位都不会被转换
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.