Coder Social home page Coder Social logo

daydayup's People

Stargazers

 avatar  avatar

daydayup's Issues

多维数组转一维数组

  1. 使用数组map()方法,对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
let arr = [1,[2,[[3,4],5],6]];
function oneDimensionalArray (arr) {
        let arr1 = (arr + '').split(','); // 将数组转字符串后再以逗号分隔转为数组
        let arr2 = arr1.map(function(x){
            return Number(x);
        });
        return arr2;
}
console.log(oneDimensionalArray(arr));

  1. 使用apply结合concat,缺点是只能将二维转一维,多维数组就不对了。
let arr = [1,[2,3],[4,5]];
console.log([].concat.apply([],arr));
  1. 将数组转为字符串再转为数组
let arr = [1,[2,[[3,4],5],6]];
let arr2 = arr.join(',').split(',');
console.log(arr2);//["1", "2", "3", "4", "5", "6"]

//或
let c=[1,3,4,5,[6,[0,1,5],9],[2,5,[1,5]],[5]];
console.log(c.toString().split(','))
  1. 递归
let arr = [1, [2, [[3, 4], 5], 6]];
let newArr = [];

function oneDimensionalArray(arr) {
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            oneDimensionalArray(arr[i]);
        } else {
            newArr.push(arr[i]);
        }
    }
}
oneDimensionalArray(arr);
console.log(newArr); //[1, 2, 3, 4, 5, 6]
  1. reduce+递归
use strict';
    let arr = [1,[2,[[3,4],5],6]];
    const flatten = arr => arr.reduce(
            (acc,val) => acc.concat(Array.isArray(val)? flatten(val):val),[]
    )
    console.log(flatten(arr));//[1, 2, 3, 4, 5, 6]

splice slice split

splice(para1, para2, value1, value2): 更改原数组,删除,添加
slice(para1, para2) 截取到新的数组
split: 字符串分割

一个浏览器是如何工作的?(阶段四)

接下来,浏览器的工作就是确定每一个元素的位置了。我们的基本原则仍然不变,就是尽可能流式地处理上一步骤的输出。

浏览器最基本的排版方案是正常流排版,它包含了顺次排布和折行等规则,我们把它叫做正常流。浏览器的文字排版遵循公认的文字排版规范,文字排版是一个复杂的系统,它规定了行模型和文字在行模型中的排布。行模型规定了行顶、行底、文字区域、基线等对齐方式。

浏览器又可以支持元素和文字的混排,元素被定义为占据长方形的区域,还允许边框、边距和留白,这个就是所谓的盒模型

在正常流的基础上,浏览器还支持两类元素:绝对定位元素和浮动元素。

  • 绝对定位元素把自身从正常流抽出,直接由top和left等属性确定自身的位置,不参加排版计算,也不影响其它元素。绝对定位元素由position属性控制。
  • 浮动元素则是使得自己在正常流的位置向左或者向右移动到边界,并且占据一块排版空间。浮动元素由float属性控制。

正常流文字排版

正常流是唯一一个文字和盒混排的排版方式
advance代表每一个文字排布后在主轴上的前进距离,它跟文字的宽/高不相等,是字体中最重要的属性。
除了字体提供的字形本身包含的信息,文字排版还受到一些CSS属性影响,如line-height、letter-spacing、word-spacing等。
在正常流的文字排版中,多数元素被当作长方形盒来排版,而只有display为inline的元素,是被拆成文本来排版的
display值为inline的元素中的文字排版时会被直接排入文字流中,inline元素主轴方向的margin属性和border属性(例如主轴为横向时的margin-left和margin-right)也会被计算进排版前进距离当中。

正常流中的盒

在正常流中,display不为inline的元素或者伪元素,会以盒的形式跟文字一起排版。多数display属性都可以分成两部分:内部的排版和是否inline,带有inline-前缀的盒,被称作行内级盒。
浏览器对行的排版,一般是先行内布局,再确定行的位置,根据行的位置计算出行内盒和文字的排版位置。
块级盒比较简单,它总是单独占据一整行,计算出交叉轴方向的高度即可。

绝对定位元素

position属性为absolute的元素,我们需要根据它的包含块来确定位置,这是完全跟正常流无关的一种独立排版模式,逐层找到其父级的position非static元素即可。

浮动元素排版

float元素非常特别,浏览器对float的处理是先排入正常流,再移动到排版宽度的最左/最右(这里实际上是主轴的最前和最后)。

element table 合计方法

getSummaries(param) {
  const { columns, data } = param;
  const sums = [];
  columns.forEach((column, index) => {
    if(index === 0) {
      sums[index] = '合计';
      return;
    }
    const values = data.map(item => Number(item[column.property]));
    if(!values.every(value => isNaN(value))) {
      sums[index] = values.reduce((prev, curr) =>{
        const value = Number(curr);
        if(!isNaN(value)){
          return prev + curr;
        } else {
          return prev;
        }
      }, 0);
    } else {
       sums[index] = '';
    }
  });
  return sums;
}

介绍下 Set、Map、WeakSet 和 WeakMap 的区别?

Set 和 Map 主要的应用场景在于 数据重组数据储存

Set 是一种叫做集合的数据结构,Map 是一种叫做字典的数据结构

1. 集合(Set)

ES6 新增的一种新的数据结构,类似于数组,但成员是唯一且无序的,没有重复的值。

Set 本身是一种构造函数,用来生成 Set 数据结构。

new Set([iterable])

举个例子:

const s = new Set()
[1, 2, 3, 4, 3, 2, 1].forEach(x => s.add(x))

for (let i of s) {
    console.log(i)	// 1 2 3 4
}

// 去重数组的重复对象
let arr = [1, 2, 3, 2, 1, 1]
[... new Set(arr)]	// [1, 2, 3]

Set 对象允许你储存任何类型的唯一值,无论是原始值或者是对象引用。

向 Set 加入值的时候,不会发生类型转换,所以5"5"是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===),主要的区别是**NaN等于自身,而精确相等运算符认为NaN不等于自身。**

let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}

let set1 = new Set()
set1.add(5)
set1.add('5')
console.log([...set1])	// [5, "5"]
  • Set 实例属性

    • constructor: 构造函数

    • size:元素数量

      let set = new Set([1, 2, 3, 2, 1])
      
      console.log(set.length)	// undefined
      console.log(set.size)	// 3
  • Set 实例方法

    • 操作方法
      • add(value):新增,相当于 array里的push

      • delete(value):存在即删除集合中value

      • has(value):判断集合中是否存在 value

      • clear():清空集合


        let set = new Set()
        set.add(1).add(2).add(1)
        
        set.has(1)	// true
        set.has(3)	// false
        set.delete(1)	
        set.has(1)	// false

        Array.from 方法可以将 Set 结构转为数组

        const items = new Set([1, 2, 3, 2])
        const array = Array.from(items)
        console.log(array)	// [1, 2, 3]
        // 或
        const arr = [...items]
        console.log(arr)	// [1, 2, 3]
    • 遍历方法(遍历顺序为插入顺序)
      • keys():返回一个包含集合中所有键的迭代器

      • values():返回一个包含集合中所有值得迭代器

      • entries():返回一个包含Set对象中所有元素得键值对迭代器

      • forEach(callbackFn, thisArg):用于对集合成员执行callbackFn操作,如果提供了 thisArg 参数,回调中的this会是这个参数,没有返回值

        let set = new Set([1, 2, 3])
        console.log(set.keys())	// SetIterator {1, 2, 3}
        console.log(set.values())	// SetIterator {1, 2, 3}
        console.log(set.entries())	// SetIterator {1, 2, 3}
        
        for (let item of set.keys()) {
          console.log(item);
        }	// 1	2	 3
        for (let item of set.entries()) {
          console.log(item);
        }	// [1, 1]	[2, 2]	[3, 3]
        
        set.forEach((value, key) => {
            console.log(key + ' : ' + value)
        })	// 1 : 1	2 : 2	3 : 3
        console.log([...set])	// [1, 2, 3]

        Set 可默认遍历,默认迭代器生成函数是 values() 方法

        Set.prototype[Symbol.iterator] === Set.prototype.values	// true

        所以, Set可以使用 map、filter 方法

        let set = new Set([1, 2, 3])
        set = new Set([...set].map(item => item * 2))
        console.log([...set])	// [2, 4, 6]
        
        set = new Set([...set].filter(item => (item >= 4)))
        console.log([...set])	//[4, 6]

        因此,Set 很容易实现交集(Intersect)、并集(Union)、差集(Difference)

        let set1 = new Set([1, 2, 3])
        let set2 = new Set([4, 3, 2])
        
        let intersect = new Set([...set1].filter(value => set2.has(value)))
        let union = new Set([...set1, ...set2])
        let difference = new Set([...set1].filter(value => !set2.has(value)))
        
        console.log(intersect)	// Set {2, 3}
        console.log(union)		// Set {1, 2, 3, 4}
        console.log(difference)	// Set {1}

2. WeakSet

WeakSet 对象允许你将弱引用对象储存在一个集合中

WeakSet 与 Set 的区别:

  • WeakSet 只能储存对象引用,不能存放值,而 Set 对象都可以
  • WeakSet 对象中储存的对象值都是被弱引用的,即垃圾回收机制不考虑 WeakSet 对该对象的应用,如果没有其他的变量或属性引用这个对象值,则这个对象将会被垃圾回收掉(不考虑该对象还存在于 WeakSet 中),所以,WeakSet 对象里有多少个成员元素,取决于垃圾回收机制有没有运行,运行前后成员个数可能不一致,遍历结束之后,有的成员可能取不到了(被垃圾回收了),WeakSet 对象是无法被遍历的(ES6 规定 WeakSet 不可遍历),也没有办法拿到它包含的所有元素

属性:

  • constructor:构造函数,任何一个具有 Iterable 接口的对象,都可以作参数

    const arr = [[1, 2], [3, 4]]
    const weakset = new WeakSet(arr)
    console.log(weakset)

2019-03-08 9 24 34

方法:

  • add(value):在WeakSet 对象中添加一个元素value
  • has(value):判断 WeakSet 对象中是否包含value
  • delete(value):删除元素 value
  • clear():清空所有元素,注意该方法已废弃
var ws = new WeakSet()
var obj = {}
var foo = {}

ws.add(window)
ws.add(obj)

ws.has(window)	// true
ws.has(foo)	// false

ws.delete(window)	// true
ws.has(window)	// false

3. 字典(Map)

集合 与 字典 的区别:

  • 共同点:集合、字典 可以储存不重复的值
  • 不同点:集合 是以 [value, value]的形式储存元素,字典 是以 [key, value] 的形式储存
const m = new Map()
const o = {p: 'haha'}
m.set(o, 'content')
m.get(o)	// content

m.has(o)	// true
m.delete(o)	// true
m.has(o)	// false

任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数,例如:

const set = new Set([
  ['foo', 1],
  ['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1

const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3

如果读取一个未知的键,则返回undefined

new Map().get('asfddfsasadf')
// undefined

注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要非常小心。

const map = new Map();

map.set(['a'], 555);
map.get(['a']) // undefined

上面代码的setget方法,表面是针对同一个键,但实际上这是两个值,内存地址是不一样的,因此get方法无法读取该键,返回undefined

由上可知,Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。

如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,比如0-0就是一个键,布尔值true和字符串true则是两个不同的键。另外,undefinednull也是两个不同的键。虽然NaN不严格相等于自身,但 Map 将其视为同一个键。

let map = new Map();

map.set(-0, 123);
map.get(+0) // 123

map.set(true, 1);
map.set('true', 2);
map.get(true) // 1

map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3

map.set(NaN, 123);
map.get(NaN) // 123

Map 的属性及方法

属性:

  • constructor:构造函数

  • size:返回字典中所包含的元素个数

    const map = new Map([
      ['name', 'An'],
      ['des', 'JS']
    ]);
    
    map.size // 2

操作方法:

  • set(key, value):向字典中添加新元素
  • get(key):通过键查找特定的数值并返回
  • has(key):判断字典中是否存在键key
  • delete(key):通过键 key 从字典中移除对应的数据
  • clear():将这个字典中的所有元素删除

遍历方法

  • Keys():将字典中包含的所有键名以迭代器形式返回
  • values():将字典中包含的所有数值以迭代器形式返回
  • entries():返回所有成员的迭代器
  • forEach():遍历字典的所有成员
const map = new Map([
            ['name', 'An'],
            ['des', 'JS']
        ]);
console.log(map.entries())	// MapIterator {"name" => "An", "des" => "JS"}
console.log(map.keys()) // MapIterator {"name", "des"}

Map 结构的默认遍历器接口(Symbol.iterator属性),就是entries方法。

map[Symbol.iterator] === map.entries
// true

Map 结构转为数组结构,比较快速的方法是使用扩展运算符(...)。

对于 forEach ,看一个例子

const reporter = {
  report: function(key, value) {
    console.log("Key: %s, Value: %s", key, value);
  }
};

let map = new Map([
    ['name', 'An'],
    ['des', 'JS']
])
map.forEach(function(value, key, map) {
  this.report(key, value);
}, reporter);
// Key: name, Value: An
// Key: des, Value: JS

在这个例子中, forEach 方法的回调函数的 this,就指向 reporter

与其他数据结构的相互转换

  1. Map 转 Array

    const map = new Map([[1, 1], [2, 2], [3, 3]])
    console.log([...map])	// [[1, 1], [2, 2], [3, 3]]
  2. Array 转 Map

    const map = new Map([[1, 1], [2, 2], [3, 3]])
    console.log(map)	// Map {1 => 1, 2 => 2, 3 => 3}
  3. Map 转 Object

    因为 Object 的键名都为字符串,而Map 的键名为对象,所以转换的时候会把非字符串键名转换为字符串键名。

    function mapToObj(map) {
        let obj = Object.create(null)
        for (let [key, value] of map) {
            obj[key] = value
        }
        return obj
    }
    const map = new Map().set('name', 'An').set('des', 'JS')
    mapToObj(map)  // {name: "An", des: "JS"}
  4. Object 转 Map

    function objToMap(obj) {
        let map = new Map()
        for (let key of Object.keys(obj)) {
            map.set(key, obj[key])
        }
        return map
    }
    
    objToMap({'name': 'An', 'des': 'JS'}) // Map {"name" => "An", "des" => "JS"}
  5. Map 转 JSON

    function mapToJson(map) {
        return JSON.stringify([...map])
    }
    
    let map = new Map().set('name', 'An').set('des', 'JS')
    mapToJson(map)	// [["name","An"],["des","JS"]]
  6. JSON 转 Map

    function jsonToStrMap(jsonStr) {
      return objToMap(JSON.parse(jsonStr));
    }
    
    jsonToStrMap('{"name": "An", "des": "JS"}') // Map {"name" => "An", "des" => "JS"}

4. WeakMap

WeakMap 对象是一组键值对的集合,其中的键是弱引用对象,而值可以是任意

注意,WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。

WeakMap 中,每个键对自己所引用对象的引用都是弱引用,在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),所以,WeakMap 的 key 是不可枚举的。

属性:

  • constructor:构造函数

方法:

  • has(key):判断是否有 key 关联对象
  • get(key):返回key关联对象(没有则则返回 undefined)
  • set(key):设置一组key关联对象
  • delete(key):移除 key 的关联对象
let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();

myWeakmap.set(myElement, {timesClicked: 0});

myElement.addEventListener('click', function() {
  let logoData = myWeakmap.get(myElement);
  logoData.timesClicked++;
}, false);

5. 总结

  • Set
    • 成员唯一、无序且不重复
    • [value, value],键值与键名是一致的(或者说只有键值,没有键名)
    • 可以遍历,方法有:add、delete、has
  • WeakSet
    • 成员都是对象
    • 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
    • 不能遍历,方法有add、delete、has
  • Map
    • 本质上是键值对的集合,类似集合
    • 可以遍历,方法很多可以跟各种数据格式转换
  • WeakMap
    • 只接受对象作为键名(null除外),不接受其他类型的值作为键名
    • 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
    • 不能遍历,方法有get、set、has、delete

6. 扩展:Object与Set、Map

  1. Object 与 Set

    // Object
    const properties1 = {
        'width': 1,
        'height': 1
    }
    console.log(properties1['width']? true: false) // true
    
    // Set
    const properties2 = new Set()
    properties2.add('width')
    properties2.add('height')
    console.log(properties2.has('width')) // true
  2. Object 与 Map

JS 中的对象(Object),本质上是键值对的集合(hash 结构)

const data = {};
const element = document.getElementsByClassName('App');

data[element] = 'metadata';
console.log(data['[object HTMLCollection]']) // "metadata"

但当以一个DOM节点作为对象 data 的键,对象会被自动转化为字符串[Object HTMLCollection],所以说,Object 结构提供了 字符串-值 对应,Map则提供了 值-值 的对应

本文始发于我的博客:Set、WeakSet、Map及WeakMap

Originally posted by @sisterAn in Advanced-Frontend/Daily-Interview-Question#6 (comment)

element 点击展开单行控制

通过template扩展

1、table 部分

:row-key='getRowKeys'
:expand-row-keys="expands"
@expand-change="expandSelect"
2、column 部分 :参见官方示例

XXXX 3、data 部分

data:{
expands: [],
},
4、method 部分

【注】设置展开区域的具体内容,就是在 expandSelect 这个方法内设置

getRowKeys:function(row){
return row.id
},
expandSelect:function(row, expandedRows) {
var that = this
if (expandedRows.length) {
that.expands = []
if (row) {
that.expands.push(row.id)
}
}
else {
that.expands = []
}
},

webpack中loader和plugin

对于loader,它就是转换器,把A文件进行编译转换成B文件,比如将A.scss或A.less转变为B.css,单纯的文件转换过程;
对于plugin,它就是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,例如

  • run:开始编译
  • make:从entry开始递归分析依赖并对依赖进行build
  • build-moodule:使用loader加载文件并build模块
  • normal-module-loader:对loader加载的文件用acorn编译,生成抽象语法树AST
  • program:开始对AST进行遍历,当遇到require时触发call require事件
  • seal:所有依赖build完成,开始对chunk进行优化(抽取公共模块、加hash等)
  • optimize-chunk-assets:压缩代码
  • emit:把各个chunk输出到结果文件

通过对节点的监听,从而找到合适的节点对文件做适当的处理。

vue组件间通信六种方式

组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。

vue组件间通信的几种方式,如props、 $emit/ $on、vuex、 $parent / $children、 $attrs/ $listeners和provide/inject

方法一、 props / $emit

1.父组件向子组件传值
父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed

2.子组件向父组件传值(通过事件形式)
子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

方法二、 $emit / $on

这种方法通过一个空的Vue实例作为**事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。

var Event = new Vue(); Event.$emit(事件名,数据); Event.$on(事件名,data=>{});
因为有时不确定何时会触发事件,一般会在 mounted 或 created 钩子中来监听。

方法三、vuex

方法四、 $attrs / $listeners

方法五、provide/inject

方法六、 $parent / $children & ref

宏任务微任务

采纳 JSC 引擎的术语,我们把宿主发起的任务
称为宏观任务,把 JavaScript 引擎发起的任务称为微观任务。

函数参数解构赋值

[[1,2],[3,4]].map(([a,b]) => a+b);
// [3,7]

function move({ x = 0, y = 0 } = {}) {
  return [x, y]
}
move({x: 3, y: 4}); // [3, 4]
move({x:3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

// 和下面的写法不同

function move({x, y} = {x: 0, y: 0}) {
  return [x, y]
}
move ({x: 3, y: 4}); // [3, 4]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

// 上面的代码为 move 的参数指定默认值
// undefined 会触发函数参数的默认值
[1, undefined, 3].map((x = 'yes') => x); // [1, 'yes', 3]

Promise

总结一下如何分析异步执行的顺序:
首先我们分析有多少个宏任务;
在每个宏任务中,分析有多少个微任务;
根据调用次序,确定宏任务中的微任务执行次序;
根据宏任务的触发规则和调用次序,确定宏任务的执行次序;
确定整个顺序

一个浏览器是如何工作的(阶段三)

把CSS规则应用到节点上,并给这棵朴素的DOM树添加上CSS属性

选择器的出现顺序,必定跟构建DOM树的顺序一致。这是一个CSS设计的原则,即保证选择器在DOM树构建到当前节点时,已经可以准确判断是否匹配,不需要后续节点信息。

CSS计算是把CSS规则应用到DOM树上,为DOM结构添加显示相关属性的过程

后代选择器 “空格”

Vue 的父组件和子组件生命周期钩子执行顺序是什么

  1. 加载渲染过程
    父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
  2. 子组件更新过程
    父beforeUpdate->子beforeUpdate->子updated->父updated
  3. 父组件更新过程
    父beforeUpdate->父updated
  4. 销毁过程
    父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

总结:从外到内,再从内到外

浏览器进程

chrome浏览器的主要进程

  • Browser进程,浏览器的主进程
  • 第三方插件进程
  • GPU进程
  • Renderer进程

@原文:https://juejin.im/post/5cda1492e51d453a8f348c02

Renderer进程的主要线程

  • GUI渲染线程
  • JavaScript引擎线程
  • 事件触发线程
  • 定时器线程
  • 异步HTTP请求线程

任务队列

事件循环

  • 第一阶段,JavaScript引擎线程从头到尾把脚本代码执行一遍,碰到需要其他线程处理的代码则交给其他线程处理。
  • 第二阶段,JavaScript引擎线程专注于处理事件。它会不断的去轮询任务队列,执行任务队列中的事件。这个过程又可以分解为轮询任务队列-执行任务队列中的事件-更新页面视图的无限往复。

宏任务

不同任务源的任务会放入不同的任务队列里,浏览器根据自己的算法来决定先取哪个队列里的任务。

宏任务至少有一个任务队列,微任务只有一个任务队列

微任务

哪些异步事件是微任务?Promise的回调、MutationObserver的回调以及nodejs中process.nextTick的回调。

宏任务和宏任务之间,积压的所有微任务会一次性执行完毕

一个浏览器是如何工作的1

对浏览器的实现者来说,他们做的事情,就是把一个URL变成一个屏幕上显示的网页。
这个过程是这样的:

  1. 浏览器首先使用HTTP协议或者HTTPS协议,向服务端请求页面;
  2. 把请求回来的HTML代码经过解析,构建成DOM树;
  3. 计算DOM树上的CSS属性;
  4. 最后根据CSS属性对元素逐个进行渲染,得到内存中的位图;
  5. 一个可选的步骤是对位图进行合成,这会极大地增加后续绘制的速度;
  6. 合成之后,再绘制到界面上。

一,通讯的部分:
HTTP协议是基于TCP协议出现的,对TCP协议来说,TCP协议是一条双向的通讯通道,HTTP在TCP的基础上,规定了Request-Response的模式。这个模式决定了通讯必定是由浏览器端首先发起的。
HTTP是纯粹的文本协议,它是规定了使用TCP协议来传输文本格式的一个应用层协议。

在HTTP协议的基础上,HTTPS和HTTP2规定了更复杂的内容,但是它基本保持了HTTP的设计**,即:使用上的Request-Response模式。

HTTPS有两个作用,一是确定请求的目标服务端身份,二是保证传输的数据不会被网络中间节点窃听或者篡改。

HTTPS是使用加密通道来传输HTTP的内容。但是HTTPS首先与服务端建立一条TLS加密通道。TLS构建于TCP协议之上,它实际上是对传输的内容做一次加密,所以从传输内容上看,HTTPS跟HTTP没有任何区别。

HTTP 2是HTTP 1.1的升级版本,HTTP 2.0 最大的改进有两点,一是支持服务端推送,二是支持TCP连接复用。

服务端推送能够在客户端发送第一个请求到服务端时,提前把一部分内容推送给客户端,放入缓存当中,这可以避免客户端请求顺序带来的并行度不高,从而导致的性能问题。

TCP连接复用,则使用同一个TCP连接来传输多个HTTP请求,避免了TCP连接建立时的三次握手开销,和初建TCP连接时传输窗口小的问题。

Vue中在新窗口打开页面 及 Vue-router

使用路由对象的resolve方法解析路由,可以得到location、router、href等目标路由的信息。得到href就可以使用window.open开新窗口了。

const {href} = this.$router.resolve({
    name: "statistics-explain",
    params: {
        classID: id,
        examSubjectID: this.planClassData.examSubjectID,
        studentStatus: 0
    }
});
window.open(href, '_blank');

node的url模块

node -pe "require('url').parse('/test?q=1', true)"

运行结果:

Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: '?q=1',
  query: { q: '1' },
  pathname: '/test',
  path: '/test?q=1',
  href: '/test?q=1' 
}

Chapter5

Q1: 为什么有的编程规范要求用 void 0 代替 undefined ?
Q2: 字符串有最大长度吗?
Q3: 0.1+0.2 为什么不等于 0.3?
Q4: ES6新加入的 Symbol 是个什么东西?
Q5: 为什么给对象添加的方法能用在基本类型上?

A1: 因为JavaScript的代码 undefined 是一个变量,而并非是一个关键字,为了避免无意中被篡改,用void 0 获取 undefined 值

A2: String 有最大长度为 2^53 - 1 但是这个所谓的最大长度 并非是理解的字符数;因为 String 的意义并非”字符串“,而是字符串的 UTF16编码。JavaScript中的字符串是永远无法变更的,一旦字符串构造出来,无法用任何方式改变字符串的内容,所以字符串具有值类型的特征。

A3: JavaScript 中的 Number 类型基本符合IEEE规定的双精度浮点数规则,JavaScript为了表达几个额外的语言场景,规定了几个例外的情况: NaN,Infinity,-Infinity。
另外要注意的是:JavaScript中有 +0 和 -0,区分 +0 和 -0的方式是检测 1/x 是 Infinity 还是 -Infinity。
根据双精度浮点数的定义 Number 类型中有效的整数范围是 -0x1fffffffffffff 至 0x1fffffffffffff, 所以 Number 无法精确表示此范围外的整数。
同样根据浮点数的定义,非整数的 Number 类型无法用 == (===也不行)来比较。这就是为什么 0.1 + 0.2 != 0.3 的原因。浮点数运算的精度问题导致两侧并不是严格相等,而是相差了很微小的值。正确的比较方法是使用JavaScript提供的最小精度值。 Math.abs(0.1+0.2-03) <=Number.EPSILON j结果为 true

A4:

Preload, prefetch and other <link> tags

<link rel="preload"> – when you’ll need a resource in a few seconds
<link rel="prefetch"> – when you’ll need a resource on a next page
<link rel="preconnect"> – when you know you’ll need a resource soon, but you don’t know its full url yet
<link rel="dns-prefetch"> – also when you know you’ll need a resource soon, but you don’t know its full url yet (for older browsers)
<link rel="prerender"> – when you’re certain users will navigate to a specific page, and you want to speed it up

URLSearchParams

var paramsString = "q=URLUtils.searchParams&topic=api"
var searchParams = new URLSearchParams(paramsString);

for (let p of searchParams) {
console.log(p);
}

searchParams.has("topic") === true; // true
searchParams.get("topic") === "api"; // true
searchParams.getAll("topic"); // ["api"]
searchParams.get("foo") === ""; // true
searchParams.append("topic", "webdev");
searchParams.toString(); // "q=URLUtils.searchParams&topic=api&topic=webdev"
searchParams.set("topic", "More webdev");
searchParams.toString(); // "q=URLUtils.searchParams&topic=More+webdev"
searchParams.delete("topic");
searchParams.toString(); // "q=URLUtils.searchParams"

会把axios 默认的 Content-Type: application/json 修改为 ‘x-www-form-urlencoded’
使用URLSearchParams,将原本默认的contenttype变为x-www-form-urlencoded,表单数据一般后端框架都能处理

call和apply的区别

记忆技巧

apply:最多只能有两个参数——新this对象和一个数组argArray
call:它可以接受多个参数,第一个参数与apply一样,后面则是一串参数列表。

实际上,apply和call的功能是一样的,只是传入的参数列表形式不同。

新特性:async/await

async/await 是 ES2016 新加入的特性,它提供了用 for、if 等代码结构来编写异步的方
式。它的运行时基础是 Promise
async 函数必定返回 Promise,我们把所有返回 Promise 的函数都可以认为是异步函数。
async 函数是一种特殊语法,特征是在 function 关键字之前加上 async 关键字,这样,
就定义了一个 async 函数,我们可以在其中使用 await 来等待一个 Promise。
async 函数强大之处在于,它是可以嵌套的。我们在定义了一批原子操作的情况下,可以
利用 async 函数组合出新的 async 函数。

对象的键名的转换

  • 对象的键名只能是字符串和Symbol类型

  • 其他类型的键名会被转化为字符串类型

  • 对象转字符串会默认调用 toString 方法

git仓库迁移

git remote -v 查看远程仓库信息
git remote set-url origin [new_remote_repository_address] 修改关联远程仓库地址

https的工作原理

HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL协议不仅仅是一套加密传输的协议,更是一件经过艺术家精心设计的艺术品,TLS/SSL中使用了非对称加密,对称加密以及HASH算法。握手过程的简单描述如下:

浏览器将自己支持的一套加密规则发送给网站。

网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。

获得网站证书之后浏览器要做以下工作:

a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。

如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。

使用约定好的HASH计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。

4.网站接收浏览器发来的数据之后要做以下的操作:

a) 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。

b) 使用密码加密一段握手消息,发送给浏览器。

5.浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。

使用命令行打开vscode

  1. 手动打开vscode
  2. command + shift + p 打开命令面板(或者点击菜单栏 查看>命令面板)
  3. 输入shell(选择"install code command in PATH")
  4. 打开终端 进入需要用IDE打开的文件夹 输入"code ."

写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?

key是给每一个vnode的唯一id,可以依靠key,更准确, 更快的拿到oldVnode中对应的vnode节点。

  1. 更准确
    因为带key就不是就地复用了,在sameNode函数 a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。
  2. 更快
    利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。

vue和react都是采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中(建议先了解一下diff算法过程)。
在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种是map映射,另一种是遍历查找。相比而言。map映射的速度更快。

['1', '2', '3'].map(parseInt) what & why ?

这是今天在 Advanced-Frontend组织 看到一个比较有意思的题目。
主要是讲JS的映射与解析
早在 2013年, 加里·伯恩哈德就在微博上发布了以下代码段:

['10','10','10','10','10'].map(parseInt);
// [10, NaN, 2, 3, 4]

parseInt

parseInt() 函数解析一个字符串参数,并返回一个指定基数的整数 (数学系统的基础)。

const intValue = parseInt(string[, radix]);

string 要被解析的值。如果参数不是一个字符串,则将其转换为字符串(使用 ToString 抽象操作)。字符串开头的空白符将会被忽略。

radix 一个介于2和36之间的整数(数学系统的基础),表示上述字符串的基数。默认为10。
返回值 返回一个整数或NaN

parseInt(100); // 100
parseInt(100, 10); // 100
parseInt(100, 2); // 4 -> converts 100 in base 2 to base 10

注意:
radix为 undefined,或者radix为 0 或者没有指定的情况下,JavaScript 作如下处理:

  • 如果字符串 string 以"0x"或者"0X"开头, 则基数是16 (16进制).
  • 如果字符串 string 以"0"开头, 基数是8(八进制)或者10(十进制),那么具体是哪个基数由实现环境决定。ECMAScript 5 规定使用10,但是并不是所有的浏览器都遵循这个规定。因此,永远都要明确给出radix参数的值。
  • 如果字符串 string 以其它任何值开头,则基数是10 (十进制)。

更多详见parseInt | MDN

map

map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

var new_array = arr.map(function callback(currentValue[,index[, array]]) {
 // Return element for new_array
 }[, thisArg])

可以看到callback回调函数需要三个参数, 我们通常只使用第一个参数 (其他两个参数是可选的)。
currentValue 是callback 数组中正在处理的当前元素。
index可选, 是callback 数组中正在处理的当前元素的索引。
array可选, 是callback map 方法被调用的数组。
另外还有thisArg可选, 执行 callback 函数时使用的this 值。

const arr = [1, 2, 3];
arr.map((num) => num + 1); // [2, 3, 4]

更多详见Array.prototype.map() | MDN

回到真实的事例上

回到我们真实的事例上

['1', '2', '3'].map(parseInt)

对于每个迭代map, parseInt()传递两个参数: 字符串和基数
所以实际执行的的代码是:

['1', '2', '3'].map((item, index) => {
	return parseInt(item, index)
})

即返回的值分别为:

parseInt('1', 0) // 1
parseInt('2', 1) // NaN
parseInt('3', 2) // NaN, 3 不是二进制

所以:

['1', '2', '3'].map(parseInt)
// 1, NaN, NaN

由此,加里·伯恩哈德例子也就很好解释了,这里不再赘述

['10','10','10','10','10'].map(parseInt);
// [10, NaN, 2, 3, 4]

如何在现实世界中做到这一点

如果您实际上想要循环访问字符串数组, 该怎么办? map()然后把它换成数字?使用编号!

['10','10','10','10','10'].map(Number);
// [10, 10, 10, 10, 10]

本文始发于我的博客:['1', '2', '3'].map(parseInt) what & why ?

Originally posted by @sisterAn in Advanced-Frontend/Daily-Interview-Question#4 (comment)

一个浏览器是如何工作的2

解析代码
image

1.词(token)是如何被拆分的

浏览器是如何用代码实现,我们设想,代码开始从HTTP协议收到的字符流读取字符。我们每读入一个字符,其实都要做一次决策,而且这些决定是跟“当前状态”有关的。在这样的条件下,浏览器工程师要想实现把字符流解析成词(token),最常见的方案就是使用状态机。

2.状态机
绝大多数语言的词法部分都是用状态机实现的。

状态机的初始状态,我们仅仅区分 “< ”和 “非<”:

  • 如果获得的是一个非<字符,那么可以认为进入了一个文本节点;
  • 如果获得的是一个<字符,那么进入一个标签状态。

用状态机做词法分析,其实正是把每个词的“特征字符”逐个拆开成独立状态,然后再把所有词的特征字符链合并起来,形成一个联通图结构。
接下来就是代码实现的事情了,在C/C++和JavaScript中,实现状态机的方式大同小异:我们把每个函数当做一个状态,参数是接受的字符,返回值是下一个状态函数。

词法分析器接受字符的方式很简单,就像下面这样:
`function HTMLLexicalParser(){

//状态函数们……
function data() {
    // ……
}

function tagOpen() {
    // ……
}
// ……
var state = data;
this.receiveInput = function(char) {
    state = state(char);
}

}`
至此,我们就把字符流拆成了词(token)了。

构建DOM树
接下来我们要把这些简单的词变成DOM树,这个过程我们是使用栈来实现的,任何语言几乎都有栈
function HTMLSyntaticalParser(){ var stack = [new HTMLDocument]; this.receiveInput = function(token) { //…… } this.getOutput = function(){ return stack[0]; } }
通过这个栈,我们可以构建DOM树:

  • 栈顶元素就是当前节点;
  • 遇到属性,就添加到当前节点;
  • 遇到文本节点,如果当前节点是文本节点,则跟文本节点合并,否则入栈成为当前节点的子节点;
  • 遇到注释节点,作为当前节点的子节点;
  • 遇到tag start就入栈一个节点,当前节点就是这个节点的父节点;
  • 遇到tag end就出栈一个节点(还可以检查是否匹配)。

https对称加密和非对称加密

对称加密:
发送方和接收方需要持有同一把密钥,发送消息和接收消息均使用该密钥。相对于非对称加密,对称加密具有更高的加解密速度,但双方都需要事先知道密钥,密钥在传输过程中可能会被窃取,因此安全性没有非对称加密高。

非对称加密:
接收方在发送消息前需要事先生成公钥和私钥,然后将公钥发送给发送方。发送放收到公钥后,将待发送数据用公钥加密,发送给接收方。接收到收到数据后,用私钥解密。
在这个过程中,公钥负责加密,私钥负责解密,数据在传输过程中即使被截获,攻击者由于没有私钥,因此也无法破解。
非对称加密算法的加解密速度低于对称加密算法,但是安全性更高。

几个名词

  • RSA:非对称加密
  • AES:对称加密 生成一个随机字符串key 只有客户端和服务端有 他们两个通过这个key对数据加密和传输跟解密 这一个统称对称加密
  • CA:权威认证机构 服务器在建站的时候 去CA认证机构认证 得到对应的数字签名 相当于身份证号 客户端每次安装浏览器的时候 都会下载最新的CA列表 这个列表有对应的数字签名和服务器IP一一对应的列表 这就是为什么我们自己搭建的localhost没法发https的原因 因为没法进行CA认证
  • 数字证书:包含了数字签名跟RSA公钥
  • 数字签名:保证数字证书一定是服务器传给客户端的 相当于服务器的身份证ID
  • 对称密钥: 对数据进行加密的key
  • 非对称密钥: (k1, k2) k1加密的数据 只有k2能解开 k1位非对称公钥 k2为非对称私钥
  • 非对称公钥:RSA公钥 k1加密的数据 只有k2能解开
  • 非对称私钥:RSA私钥 k1加密的数据 只有k2能解开

Promise

Promise是一个表现为状态机的异步容器。

它有以下几个特点:

  • 状态不受外界影响。Promise只有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。状态只能通过Promise内部提供的resolve()和reject()函数改变。

  • 状态只能从pending变为fulfilled或者从pending变为rejected。并且一旦状态改变,状态就会被冻结,无法再次改变。

  • 如果状态发生改变,任何时候都可以获得最终的状态,即便改变发生在前。这与事件监听完全不一样,事件监听只能监听之后发生的事件。

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.