Giter Club home page Giter Club logo

tianleyy.github.io's People

Contributors

tianley avatar

Watchers

James Cloos avatar  avatar

tianleyy.github.io's Issues

JavaScript趣闻

在JavaScript学习的过程中,遇见一些很有意思的文章,但是在分类的时候,不知道怎么将其分类(归根到底就是菜)。
现阶段,结合自己的实际情况,先将这些有意思的文章,都归结在这里,看完之后做一下知识小结,记录一下。

参考文章

JavaScript 工作原理

探索JS引擎工作原理

编写高质量的JS函数

如何编写高质量的 JS 函数(1) -- 敲山震虎篇
如何编写高质量的 JS 函数(2) -- 命名/注释/鲁棒篇
如何编写高质量的 JS 函数(3) --函数式编程[理论篇]
如何编写高质量的 JS 函数(4) --函数式编程[实战篇]

JS中的面向对象编程与函数式编程

JavaScript 函数式编程
彻底理解 JS 面向对象编程(OOP)

JavaScript的深拷贝与浅拷贝

初识JavaScript的深拷贝与浅拷贝

image

JavaScript的复制方式

JavaScript一般有按值传递和按引用传递两种复制方式

按值传递

按值传递的是基本数据类型, 一般存放于内存中的栈区,存取速度快,存放量小。

按引用传递

按引用传递的是引用类型,一般存放于内存中的堆区,存取速度慢,存放量大,其引用指针存于栈区,并指向引用本身。

深拷贝与浅拷贝是相对于引用类型而言的

浅拷贝:指两个JS对象指向同一个内存地址,其中一个改变会影响另外一个;
深拷贝:指复制后的新对象指向一个新的内存地址,两个对象改变互不影响。

深拷贝涉及到的范围很广,包括函数的拷贝、类的拷贝、Promise对象的拷贝等;
现在在开发中遇见的拷贝,主要是Json数据对象为主,其他的暂时没有遇见。

浅拷贝

  1. 简单的赋值操作
const obj = {
  name: 'JavaScript',
  type: 'FrontEnd'
}
const copyObj = obj // 赋值操作就是一种简单的浅拷贝
  1. Object.assign()方法

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)
  1. $.extend({}, obj)
    JQuery的实现方法,使用递归思路实现深拷贝与浅拷贝。

小结

浅拷贝是我们经常使用的操作一些对象或数组的有效方法,具体的使用需要结合实际场景来合理使用,同时需要考虑一些兼容性问题(IE的版本是否兼容)。 但是在大多数实际场景中,我们需要使用更多的是深拷贝,尤其是在各种MVVM框架框架中,引入了状态管理,例如vue的store,更多的体现在数据流中希望我们不改变原对象。这种使用就需要通过深拷贝的方式去实现。

深拷贝

JSON.parse(JSON.stringify(obj))

能够处理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)

小结

  1. 一维数据结构的深拷贝方法建议使用Object.assign()
  2. 二维数据结构及以上的深拷贝方法建议使用JSON.parse(JSON.stringify())
  3. 特别复杂的数据结构的深拷贝方法建议使用Loadsh.cloneDeep()(暂未使用过)

数组的拷贝

通过concat()slice()、拓展运算符进行的拷贝都是浅拷贝

Array.prototype.concat()

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)

Array.prototype.slice()

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)

参考文章

一文彻底理解JavaScript的深拷贝与浅拷贝

Markdown语法

准备使用Issues来记录学习过程中的一些收获,所以熟悉一下Markdown的语法。
Markdown的语法相对比较简单,熟悉语法的写法之后即可快速上手。
在Issues编辑发布的右上角也有常用语法的快捷方式,使用非常便捷。

常用的Markdown语法

Markdown语法

最常用语法

  • 代码
  • 区块
  • 列表
  • 链接
  • 图片
  • 表格

熟悉语法

段落

  1. 段落换行使用两个以上空格加上回车
  2. 在段落后面使用一个空行来表示重新开始一个段落

分割线

在一行中使用三个以上 星号、减号、底线 来建立一个分割线,行内不能有其他东西


删除线

在文字的两端加上两个 波浪线

删除线


列表

无序列表 星号、加号、减号后加空格,再填写内容

  • 第一列
  • 第二列
  • 第三列

有序列表 数字后加上点号加空格,再填写内容

  1. First
  2. second
  3. third
  4. five(根据语法,这是第4项,所以会显示出来的是4)

列表嵌套

  1. 第一列
    • 子列
      • 再嵌套?
  2. 第二列
    • 子列

区块

这是一个区块 -- >加空格,再填写内容

  1. 列表中使用区块

    在列表中使用区块
    4个空格可以让区块缩进


代码

  1. 段落上的一个函数或片段的代码 -- 用反引号包起来
    console.log 函数

  2. 代码区块 -- 三个反引号包裹一段代码并指定一种语言(可以不指定)
    ``` 包裹一段代码 -- 指定语言后代码会提示高亮

console.log(`name is tianle`)
console.log('Jos is an enginner')

链接

链接的使用方法 -- [链接名称](链接地址)

Markdown语法 - 菜鸟教程


图片

图片的使用方法 -- ![alt 属性文本](图片地址 "可选标题")

  1. 开头一个感叹号
  2. 接着一个方括号,里面放上图片的替代文字
  3. 接着一个普通括号,里面放上图片的地址 -- 还可以用引号包住title属性的文字

表格

  1. 制作表格使用 | 来分隔不同的单元格,使用 - 来分隔表头和其他行
  2. 设置对其方式
    • :- 左对齐
    • -: 右对齐
    • :-: 居中对齐
first second
在Markdown中使用表格感觉非常不方便 2
在Markdown中使用表格感觉非常不方便 2

转义

Markdown使用了很多特殊符号表示特殊的意义
如果需要显示特定的符号需要使用转义字符
Markdown 使用反斜杠转义特殊字符 \

JavaScript的语言类型

JavaScript的语言类型

Number, String, Boolean, Undefined, Null, Symbol, Object

Number

Number

String

String

总纲

JavaScript的语言类型

探索JS引擎工作原理

探索JS引擎工作原理

JavaScript从定义到执行,JS引擎在实现层做了很多初始化工作。

在学习JS引擎工作机制之前,需要引入几个相关的概念,这些组件是JS引擎工作的核心组件。

  • 执行环境栈 - ECStack
  • 全局对象 - GO
  • 执行环境 - EC
  • 变量对象 - VO
  • 活动对象 - AO
  • 作用域
  • 作用域链

通过Demo展开分析

我们将从全局初始化、执行函数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引擎在进入一段可执行的代码时,需要完成以下三个初始化工作

  1. 创建一个全局对象(Global Object)
  2. 构建一个执行环境栈(Execution Context Stack)、全局执行环境(Execution Context, EC),并将EC压入执行环境栈中。
  3. 创建一个与全局执行环境(EC)关联的全局变量对象(Varibale Object, VO),并把VO指向全局对象(GO)

创建全局对象(Global Obect, GO)

全局对象只存在一份,它的属性在任何地方都可以访问,它的存在伴随着应用程序的整个生命周期。

全局对象在创建时,将Math, String, Date, document等常用的JS对象作为其属性。
全局对象不能通过名字直接访问,因此使用属性window,并将window指向自身,就可以通过window访问这个全局对象了。

// 伪代码 -- 创建全局对象
var globalObject = {
  Math: {},
  String: {},
  Date: {},

  document: {}, // DOM操作
  window: this // 让window属性指向自身

  ... // 其他的属性
}

创建执行环境栈(ECStack)、全局执行环境(EC)

执行环境栈的作用是保证程序能够按照正确的顺序被执行(栈的特性为后进先出)

在JavaScript中,每个函数都有自己的执行环境。
当执行一个函数时,该函数的执行环境就会被推入执行环境栈的顶部并获取执行权。
当这个函数执行完毕,它的执行环境又从这个栈的顶部删除,并把执行权还给之前的执行环境。

Snipaste_2020-07-09_10-47-45

// 伪代码 -- 模拟执行环境栈和全局执行环境(EC)的关系
var ECStack = [] // 定义一个执行环境栈,类似于数组
var EC = {} // 创建一个执行空间 --> 在内存中分配一块空间

ECStack.push(EC) // 进入函数 -> 从执行栈的尾部添加EC的执行空间
ECStack.pop(EC) // 函数返回后 -> 从执行栈的尾部删除EC的执行空间

创建全局变量对象(VO)

创建一个与全局执行环境(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

执行进入A(1)时,JS引擎需要完成以下工作:

  1. JS引擎会创建函数A的执行环境EC(A),然后EC(A)推入执行环境栈的顶部并获取执行权。

    • 此时执行环境栈中有两个执行环境,分别是全局执行环境和函数A的执行环境,EC(A)在栈顶,EC(G)在栈底。
    • 创建函数A的作用域链(SC)

    在JS中,每个执行环境都有自己的作用域链,用于标识符解析。
    执行环境被创建时,它的作用域链就初始化为当前运行函数 scope 所包含的对象。

  2. JS引擎会创建一个当前函数的活动对象(AO) => 活动对象扮演着变量对象的角色

    • 活动对象(AO)包含函数的形参、arguments对象、this对象,局部变量和内部函数的定义,AO被推入作用域的顶端。
    • 在定义函数B时,JS引擎同样会为B添加一个 scope 属性,并将 scope 指向定义函数B时所在的环境 => 函数A的活动对象AO
    • 此时AO位于链表的前端,由于链表具有首位相连的特点,因此函数B的 scope指向 A 的整个作用域链。
// 运行函数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)
    }
  }
]

执行函数B

函数A被执行之后,返回函数B的引用,并赋值给变量C, 执行C(1) 就相当于执行 B(1).

JS引擎需要完成如下工作:

  1. 创建函数B的执行环境EC(B), 然后将EC(B)推入执行环境栈的顶部并获取执行权。

    • 此时执行环境栈有两个执行环境:EC(B)在栈顶,EC(G)在栈底。
    • 当函数A返回后,A的执行环境EC(A)就会从执行环境栈中删除,只留下全局执行环境EC(G)
  2. 创建函数B的作用域链,并初始化为函数B的 scope 所包含的对象,即包含了A的作用域链。

  3. 创建函数B的活动对象AO(B),并将B的形参 z, arguments对象 和 this对象作用AO(B)的属性。

当函数B执行"x+y+z"时,需要对x, y, z三个标识符进行一一解析
解析过程遵循变量查找规则:

  • 先查找自己的活动对象中是否存在该属性,如果存在,则停止查找并返回
  • 如果不存在,继续沿着作用域链从顶端开始依次查找,直到找到为止
  • 如果整个作用域链上都未找到该变量,则返回"undefined"

函数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引擎的工作原理,还是懵懵懂懂。看完之后,能了解一点点,JS真正的性能优化的一些原理,而不仅仅是图片压缩以所见请求时间之类的优化。

Originally posted by @Tianleyy in #5 (comment)

移动端的页面自适应

postcss-px-to-viewport

不需要手动进行换算
通过配置控制不想进行转换的属性或类选择器
css代码中,只需要使用一种单位 px 即可

在 vue 项目中使用 postcss-px-to-viewport

1、通过 npm 安装依赖

npm install postcss-px-to-viewport

2、添加配置文件 postcss.config.js

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 // 横屏时使用的视口宽度,同设计稿对应即可
    }
  }
}

3、重新运行项目,使配置文件生效

修改了项目的配置文件之后,必须重新运行项目,使新的配置项生效。


注意事项

搭配UI框架使用

移动端项目一般都会使用到UI框架,像我们之前使用到 vant,vant是根据 375px 的设计稿去实现的。所以建议将 viewportWidth 设置 375 即可。

配置的注意事项

propList

有一些属性的单位,我们不希望转换的时候,可以添加在数组后面,并在前面加上 ! 号

propList: ['*', '!letter-spacing'] // 表示所有css属性的属性单位都进行转化,但 letter-spacing 除外
selectorBlackList

转换的黑名单,在黑名单中添加字符串,只要类名包含该字符串,就不会被匹配。

selectorBlackList: ['wrap'] // 表示如 wrap, my-wrap, wrapper 这样的类名单位都不会被转换

参考文章

移动端布局之 posecss-px-to-viewport (兼容vant)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.