前端知识点

1.TCP有哪些手段保证可靠交付

  • TCP提供一种面向连接的、可靠的字节流服务。 面向连接:意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个TCP连接。在一个TCP连接中,仅有两方进行彼此通信。广播和多播不能用于TCP。

TCP通过下列方式来提供可靠性

  1. 应用数据被分割成TCP认为最适合发送的数据块(将数据截断为合理的长度)
  2. 发送一个报文段(TCP传递给IP信息的单位)后启动定时器,如果无法及时收到确认,重发报文(超时重发)
  3. 收到报文后,推迟几分之一秒发送确认(对于收到的请求,给出确认响应)
  4. 提供首部和校验和,校验和出错不确认收到此报文,引发重发校验出包有错,丢弃报文段,不给出响应,TCP发送数据端,超时时会重发数据)
  5. 必要情况TCP会对收到的数据重排序,将正确的顺序交给应用层(对失序数据进行重新排序,然后才交给应用层)
  6. 接收端会丢弃重复的数据(对于重复数据,能够丢弃重复数据)
  7. 本身能做流量控制(TCP可以进行流量控制,防止较快主机致使较慢主机的缓冲区溢出)

引用原文

2.ES6的Set内部实现

  • Set类就是数据结构中的集合
  • Set类的基本操作的实现:
function Set(){
    var items = {}
    var length = 0;
    //判断元素是否存在
    this.has = function(val){
        return items.hasOwnProperty(val)
    }
    //增加操作
    this.add = function(val){
        if(!this.has(val)){
            items[val] = val;
            length++;
            return true;
        }
        return false;
    }
    // 删除操作
    this.remove = function(val){
        if(this.has(val)){
            delete items[val]
            length-=1;
            return true;                
        }
        return false;
    }
    // 清除
    this.clear = function(){
        items = {};
        length = 0
        return true
    }
    //获取大小
    this.size = function(){
        return length;
    }
    //获取属性
    this.values = function(){
        return Object.keys(items);
    }
}    
var set = new Set()
set.add(1);set.add(2);set.add(3);set.add('a')        

 
  • 求并集:
this.union = function(otherSet){
    var unionSet = new Set();//存放结果
    var values = this.values();
    for(var i = 0;i<values.length;i++){
        unionSet.add(values[i]);                //放入当前集合中的元素
    }
    values = otherSet.values();
    for(var i = 0;i<values.length;i++){
        unionSet.add(values[i])                   //放入另一个集合的元素
    }
    return unionSet;
}
  • 交集:
this.intersection = function(otherSet){
    var intersectionSet = new Set();//存放结果
    var values = this.values();
    for(var i = 0;i<values.length;i++){
        if(otherSet.has(values[i])){             //只放入两个集合共有的元素
            intersectionSet.add(values[i])
        }
    }
    return intersectionSet;
}
  • 差集:
 this.difference = function(otherSet){
    var differenceSet = new Set();//存放结果
    var values = this.values();
    for(var i = 0;i<values.length;i++){
        if(!otherSet.has(values[i])){          //只放入集合otherSet中没有的
            differenceSet.add(values[i])
        }
    }
    return differenceSet;
}

3.webpack loader和plugin编写

原文连接

4.Promise内部实现原理

5.Vue3 proxy解决了哪些问题

利用 Proxy 手动实现一个极其简单数据的双向绑定(Object.defineProperty() 的实现方式

  • 页面结构如下:
<!--html-->
<div id="app">
    <h3 id="paragraph"></h3>
    <input type="text" id="input"/>
</div>
  • 主要还是得看逻辑部分:
//获取段落的节点
const paragraph = document.getElementById('paragraph');
//获取输入框节点
const input = document.getElementById('input');
    
//需要代理的数据对象
const data = {
    text: 'hello world'
}

const handler = {
    //监控 data 中的 text 属性变化
    set: function (target, prop, value) {
        if ( prop === 'text' ) {
                //更新值
                target[prop] = value;
                //更新视图
                paragraph.innerHTML = value;
                input.value = value;
                return true;
        } else {
            return false;
        }
    }
}

//添加input监听事件
input.addEventListener('input', function (e) {
    myText.text = e.target.value;   //更新 myText 的值
}, false)

//构造 proxy 对象
const myText = new Proxy(data,handler);

//初始化值
myText.text = data.text;    

上述我们通过Proxy 创建了 myText 实例,通过拦截 myText 中 text 属性 set 方法,来更新视图变化,实现了一个极为简单的 双向数据绑定~

6.实现filter或手写reduce

//这里提供一个filter的写法
Array.prototype.filters = function filter(callback){
	var arr = this;
	var filterArr = [];
	for(var i = 0,len = arr.length;i < len; i ++){
		if(callback(i,arr[i],arr)){
			filterArr.push(arr[i]);
		}
	}
	return filterArr;
}

function callback(index,item,arr){
	return item > 5;
}



[1,2,3,4,78].filters(callback)

7.webpack介绍

构建就是把源代码转换成发布到线上的可执行 JavaScrip、CSS、HTML 代码,包括如下内容。

  • 1.代码转换:TypeScript 编译成 JavaScript、SCSS或Less 编译成 CSS 等。
  • 2.文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
  • 3.代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
  • 4.模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
  • 5.自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器,nodemon。
  • 6.代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
  • 7.自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

构建其实是工程化、自动化思想在前端开发中的体现,把一系列流程用代码去实现,让代码自动化地执行这一系列复杂的流程。 构建给前端开发注入了更大的活力,解放了我们的生产力,更加方便了我们的开发

8.浅谈session,cookie,token,webStorage区别

  1. session

session的中文翻译是“会话”,当用户打开某个web应用时,便与web服务器产生一次session。服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。

  1. cookie

cookie是保存在本地终端的数据。cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。

  1. token

token的意思是“令牌”,是用户身份的验证方式,最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。还可以把不变的参数也放进token,避免多次查库。

  1. WebStorage

WebStorage的目的是克服由cookie所带来的一些限制,当数据需要被严格控制在客户端时,不需要持续的将数据发回服务器。
WebStorage两个主要目标:一是提供一种在cookie之外存储会话数据的路径。二是提供一种存储大量可以跨会话存在的数据的机制。
HTML5的WebStorage提供了两种API:localStorage(本地存储)和sessionStorage(会话存储)。

区别分析

cookie与session 的区别

  • cookie和session都是用来跟踪浏览器用户身份的会话方式。

  • cookie保存在浏览器端,session保存在服务器端

  • 单个cookie保存的数据不能超过4kb;session大小没有限制。

(1)cookie机制:如果不在浏览器中设置过期时间,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称会话cookie。如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消失。

Cookie是服务器发给客户端的特殊信息,cookie是以文本的方式保存在客户端,每次请求时都带上它

(2)session机制:当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含sessionid。如果有sessionid,服务器将根据该id返回对应session对象。如果客户端请求中没有sessionid,服务器会创建新的session对象,并把sessionid在本次响应中返回给客户端。通常使用cookie方式存储sessionid到客户端,在交互中浏览器按照规则将sessionid发送给服务器。如果用户禁用cookie,则要使用URL重写,可以通过response.encodeURL(url) 进行实现;API对encodeURL的结束为,当浏览器支持Cookie时,url不做任何处理;当浏览器不支持Cookie的时候,将会重写URL将SessionID拼接到访问地址后。

token 和session 的区别

session 和 token并不矛盾,作为身份认证 token安全性比session好,因为每个请求都有签名还能防止监听以及重放攻击,而session就必须靠链路层来保障通讯安全了。如上所说,如果你需要实现有状态的会话,仍然可以增加session来在服务器端保存一些状态

Session 是一种HTTP存储机制,目的是为无状态的HTTP提供的持久机制。所谓Session 认证只是简单的把User 信息存储到Session 里,因为SID 的不可预测性,暂且认为是安全的。这是一种认证手段。 而Token ,如果指的是OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对App 。

token就是令牌,比如你授权(登录)一个程序时,他就是个依据,判断你是否已经授权该软件;cookie就是写在客户端的一个txt文件,里面包括你登录信息之类的,这样你下次在登录某个网站,就会自动调用cookie自动登录用户名;session和cookie差不多,只是session是写在服务器端的文件,也需要在客户端写入cookie文件,但是文件里是你的浏览器编号.Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。

localStorage(本地存储)和sessionStorage(会话存储)

localStorage:localStorage的生命周期是永久的,关闭页面或浏览器之后localStorage中的数据也不会消失。localStorage除非主动删除数据,否则数据永远不会消失。

sessionStorage的生命周期是在仅在当前会话下有效。sessionStorage引入了一个“浏览器窗口”的概念,sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage也是不一样的。

localStorage和sessionStorage都保存在客户端,不与服务器进行交互通信

WebStorage的优点:

  • 存储空间更大:cookie为4KB,而WebStorage是5MB;

  • 节省网络流量:WebStorage不会传送到服务器,存储在本地的数据可以直接获取,也不会像cookie一样美词请求都会传送到服务器,所以减少了客户端和服务器端的交互,节省了网络流量;

  • 对于那种只需要在用户浏览一组页面期间保存而关闭浏览器后就可以丢弃的数据,sessionStorage会非常方便;

  • 快速显示:有的数据存储在WebStorage上,再加上浏览器本身的缓存。获取数据时可以从本地获取会比从服务器端获取快得多,所以速度更快;

  • 安全性:WebStorage不会随着HTTP header发送到服务器端,所以安全性相对于cookie来说比较高一些,不会担心截获,但是仍然存在伪造问题;

  • WebStorage提供了一些方法,数据操作比cookie方便;

  • setItem (key, value) —— 保存数据,以键值对的方式储存信息。

  • getItem (key) —— 获取数据,将键值传入,即可获取到对应的value值。

  • removeItem (key) —— 删除单个数据,根据键值移除对应的信息。

  • clear () —— 删除所有的数据

  • key (index) ——

9.compose函数

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}


function fristName(name){
    return name.split(' ')[0]
}
function upperCase(string){
    return string.toUpperCase()
}
function reverse(string){
    return string.split('').reverse().join('')
}

console.log(compose(fristName,upperCase,reverse)('xiao li')) // OAIX

10 找出最接近的值

尽量不使用 JS 特有的语法糖,尽量不使用如 Array.sort 等语言特有的方法。*/
const arr2 = [1, 5, 9, 15, 28, 33, 55, 78, 99];

/**
 * 返回最接近输入值的数字,如果有多个,返回最大的那个
 * @param {number} n
 * @return {number}
 */
function findNext(n, arr) {
  // your code here...
}
console.log(findNext(1, arr2)); // should print 1
console.log(findNext(44, arr2)); // should print 55
console.log(findNext(6, arr2)); // should print 5
console.log(findNext(7, arr2)); // should print 9
console.log(findNext(8, arr2)); // should print 9

const arr2 = [1, 5, 9, 15, 28, 33, 55, 78, 99];
function findNext(n, arr) {
    var result;
    if(Array.isArray(arr)){
        var len = arr.length;
        for(var i = 0; i < len; i++){
            for(var j = 0; i < len - 1 - i; i++){
                if(arr[j] > arr[j+1]) {//相邻元素两两对比
                    var temp = arr[j+1];//元素交换
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        //冒泡数组排序  
        let arr2 = [...arr];
        //考虑输入的值小于数组最小值    或者  大雨最大值的边界情况
        if(n<=arr2[0]) {
            result = arr2[0];
        }else if(arr2[arr2.length-1]<=n) {
            result = arr2[arr2.length-1]
        }else {
            for(let i=0;i<arr2.length;i++) {
                if(arr2[i]<n && arr2[i+1]>n) {
                    if((arr[i+1]-n) >(n-arr[i])){
                        result= arr2[i]
                    }else {
                        result= arr2[i+1]
                    }
                }
            }
        }
       return result;
    }
}
console.log(findNext(1, arr2)); // should print 1
console.log(findNext(44, arr2)); // should print 55
console.log(findNext(6, arr2)); // should print 5
console.log(findNext(7, arr2)); // should print 9
console.log(findNext(8, arr2)); // should print 9
console.log(findNext(0, arr2)); // should print 1
console.log(findNext(5000, arr2)); // should print 1

11.选择器考察

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        
    </style>
</head>
<body>
<!--题目说明-->
<!--将children下,第3个div子元素,背景颜色置为红色-->
<!--将children下,第2个子元素,文字颜色置为蓝色-->
<!--将children下,lang属性包含bcd,文字颜色置为绿色-->
<div>
    <div class="children">
        <div>test</div>
        <p >test</p>
        <div>test</div>
        <p>test</p>
        <div lang="abc">test</div>
        <div lang="abcd">test</div>
        <div>test</div>
        <div>test</div>
    </div>
</div>
</body>
</html>
///============================答案============================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .children>*:nth-child(2){
            color:blue
        }
        .children>div:nth-of-type(3){
            background-color:red
        }
        .children>*[lang*='bcd']{
            color:green
        }
    </style>
</head>
<body>
<!--题目说明-->
<!--将children下,第3个div子元素,背景颜色置为红色-->
<!--将children下,第2个子元素,文字颜色置为蓝色-->
<!--将children下,lang属性包含bcd,文字颜色置为绿色-->
<div>
    <div class="children">
        <div>test</div>
        <p >test</p>
        <div>test</div>
        <p>test</p>
        <div lang="abc">test</div>
        <div lang="abcd">test</div>
        <div>test</div>
        <div>test</div>
    </div>
</div>
</body>
</html>

12.用 DFS or BFS 来实现遍历DOM树

/**
 * 从页面根节点开始,遍历页面上所有 DOM 元素,并且返回每个DOM标签的名称和出现次数
 * 分别用「深度优先」和「广度优先」的策略来实现
 * @param {HTMLElement} 页面根节点
 * @return {Object} - 包含页面上所有标签名-该标签出现次数的对象,eg: { div: 10, p: 20, h1: 3 }
 */
function collectAllElements(e) {
    // your code here...
}
///============================答案============================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <div>
          <p >
              <span></span>
          </p>
          <p ></p>
          <h1 ></h1>
          <h1 ></h1>
        </div>
      </div>
</body>
<script>
// 输入节点信息
var res = {}
function collectAllElements(node) {
    let res = {}
    const loop = (node)=>{
        if(res[node.tagName.toLowerCase()]){
            res[node.tagName.toLowerCase()] = res[node.tagName.toLowerCase()]+1
        }else{
            res[node.tagName.toLowerCase()] = 1
        }
        const loopChild = ()=>{
        let length = node.children.length
            if(length){
                for(let i =0;i<length;i++){
                    loop (node.children[i])
                }
            }
        }
        loopChild()
    }
    loop(node)
    return res
}
var nodeObj =collectAllElements(document.getElementById("app"))
console.log(nodeObj)
</script>
</html>

13.深拷贝--实现一个函数,返回对象的深拷贝

const origin = {
  a: {
    b: {
      c: [1, 5, 11, 23, 422]
    }
  },
  d: function() {
    console.log('hello world');
  },
  e: new Date()
};
/**
 * 返回输入值的深拷贝对象
 * 
 * tips: 对象可能包含 Function、Date 等特殊对象
 * 
 * @param origin {any}
 * @return {Object}
 */
function deepClone(origin) {
  // your code here...
}
///============================答案============================================================

function deepClone(value) {  
    if (value == null) return value;  
    if (typeof value !== 'object') return value;
    if (value instanceof RegExp) return new RegExp(value);  
    if (value instanceof Date) return new Date(value);  
    // 我要判断 value 是对象还是数组 如果是对象 就产生对象 是数组就产生数组
    let obj = new value.constructor;  
    for(let key in value){    
        obj[key] = deepClone(value[key]); // 看一看当前的值是不是一个对象
    }  
    return obj;
}

14.Vue 双向绑定的理解

// 实现一个简单的 DOM-JS数据绑定方案,要求在 JS 中改变变量数据后 DOM 视图会自动更新
<!DOCTYPE html>
<html lang="en">
<head>
  <title>DOM-JS数据绑定方案</title>
</head>
<body>
  <div id="app">
    <h1 v-text="title"></h1>
    <p>当前时间:<span v-text="time"></span></p>
  </div>
  <script>
    function ViewBind({ el = 'body', data = {}} = {}) {
      // TODO,请在此书写代码
    }
    /**
     * step: 1
     * 调用方式类似 Vue 初始化,
     * el 代表根元素,data 中的字段会自动和 DOM 中 v-text 属性对应元素内容绑定
     **/
    const app = new ViewBind({
      el: '#app',
      data: {
        title: '这是标题',
        time: +new Date()
      }
    })
    /**
     * step: 2
     * 初始化之后页面#app显示效果如下:
      <div id="app">
        <h1 v-text="title">这是标题</h1>
        <p>当前时间戳:<span v-text="time">1522070099060</span></p>
      </div>
     * 类似于 Vue,初始化之后 app 内部有一个 data 对象,
     * 通过修改 data 对象的属性来间接修改 DOM 中挂载了对应 v-text 属性的元素内容
     **/
    setInterval(() => {
      // 定时修改页面上<span v-text="time">元素中的内容
      app.data.time = +new Date()
    }, 1000)
    /**
     * step3: 请实现上述 ViewBind 方法
     * 提示:可参考 Vue 中响应式数据绑定和指令的实现原理
     **/
  </script>
</body>
</html>

//========================================================
function ViewBind({ el = 'body', data = {} } = {}) {
      this._init({ el, data })
    }
    ViewBind.prototype._init = function (opts) {
      this.opts = opts
      this.data = opts.data;
      this.el = document.querySelector(opts.el)
      this._binding = {};
      this._obverse(this.data);
      this._complie(this.el);
    }
    ViewBind.prototype._obverse = function (obj) {
      for (key in obj) {
        if (obj.hasOwnProperty(key)) {
          let value = obj[key];
          this._binding[key] = {
            _directives: []
          };
          var binding = this._binding[key];
          //对属性进行get set劫持
          Object.defineProperty(this.data, key, {
            enumerable: true,
            configurable: true,
            get: function () {
              return value;
            },
            set: function (newVal) {
              binding._directives.forEach(function (item) {
                item.update();
              })
              if (value !== newVal) {
                value = newVal;
              }
            }
          })
        }
      }
    }
    //编译模版
    ViewBind.prototype._complie = function (root) {
      var _this = this;
      var nodes = root.children;
      for (var i = 0; i < nodes.length; i++) {
        var node = nodes[i];
        if (node.children.length) {
          this._complie(node);
        }
        if (node.hasAttribute('v-text')) {
          var attrVal = node.getAttribute('v-text');
          _this._binding[attrVal]._directives.push(new Watcher(
            'text',
            node,
            _this,
            attrVal,
            'innerHTML'
          ))
        }
      }
    }
    function Watcher(name, el, vm, exp, attr) {
      this.name = name;
      this.el = el;
      this.vm = vm;
      this.exp = exp;
      this.attr = attr;
      this.update();
    }
    Watcher.prototype.update = function () {
      this.el[this.attr] = this.vm.data[this.exp];
    }

15.关于DOM节点的深度优先和广度优先遍历

 

  • HTML的树形结构如上

深度优先遍历
对于树的深度优先遍历,执行结果应该如下:

** 采用递归方式**

 var arr=[];
    //深度优先
    function traversalDFSDOM (rootDom) {
        if(!rootDom)return;
        if(rootDom.children.length==0){
            arr.push(rootDom)//没有孩子节点,表示是个叶子节点,将节点push到数组中
            return;
        }
        arr.push(rootDom)//非孩子节点,在每次遍历它的孩子节点之前先把它push到数组中
        for(var i=0;i<rootDom.children.length;i++){
            traversalDFSDOM(rootDom.children[i])//递归调用
        }
 
    }

结果如下

  • 采用非递归的方式
//深度优先非递归
    function traversalDFSDOM(rootDom) {
        if(!rootDom)return;
        var stack=[]
        var node = rootDom;
        while(node!=null){
            arr.push(node);
            if(node.children.length>=0){
                for(let i=node.children.length-1;i>=0;i--)
                    stack.unshift(node.children[i]);
            }
            node = stack.shift()
        }
    }
    traversalDFSDOM(bodyDom)
--------------------- 

https://blog.csdn.net/lxy224/article/details/79823669

广度优先遍历

  • 对于DOM树的广度优先遍历的结果应该如下
  • 采用递归方式
var stack=[bodyDom];//bodyDom是遍历的根节点
    function traversalBFSDOM (count) {
        count = count || 0;
        if (stack[count]) {
            var children = stack[count].children;
            for (let i = 0; i < children.length; i++) {
                stack.push(children[i]);
            }
            traversalBFSDOM(++count)
        }
    }
--------------------- 
traversalBFSDOM(0)
  • 采用非递归方式
function traversalBFSDOM (rootDom) {
        if(!rootDom)return;
        arr.push(rootDom)
        var queue = [rootDom];
        while(queue.length){
            var node = queue.shift();
            if(!node.children.length){
                continue;
            }
            for(var i=0;i<node.children.length;i++){
                arr.push(node.children[i]);
                queue.push(node.children[i]);
            }
        }
    }
--------------------- 

主要采用先进先出的思想,依次遍历每个节点下的孩子节点。
运行结果如下:

16 什么是深拷贝?深拷贝和浅拷贝有什么区别?

浅拷贝是指只复制第一层对象,但是当对象的属性是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。
深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。
实现一个深拷贝:

function deepClone(obj) { //递归拷贝
    if(obj === null) return null; //null 的情况
    if(obj instanceof RegExp) return new RegExp(obj);
    if(obj instanceof Date) return new Date(obj);
    if(typeof obj !== 'object') {
        //如果不是复杂数据类型,直接返回
        return obj;
    }
    /**
     * 如果obj是数组,那么 obj.constructor 是 [Function: Array]
     * 如果obj是对象,那么 obj.constructor 是 [Function: Object]
     */
    let t = new obj.constructor();
    for(let key in obj) {
        //如果 obj[key] 是复杂数据类型,递归
        t[key] = deepClone(obj[key]);
    }
    return t;
}

17.斐波那契数列

 
斐波那契指的是这样一个数列:1、1、2、3、5、8、13、21、34......在数学上,斐波纳契数列以如下被以递归的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*);随着数列项数的增加,前一项与后一项之比越来越逼近黄金分割的数值0.6180339887..…

问题来了,怎样用代码实现一个斐波那契数列呢?下面是一些方法的总结;

  1. 方法1:递归

function fb1(n){
    if(n <= 2){
        return 1;    
    }else{
        return fb1(n-1) + fb1(n-2);
    }
}

  1. 方法2:尾调用优化
    方法1的递归方法基础上进行尾调用优化:

function fb2(n,res1 =1,res2 = 1){
    if(n <= 2){
        return res2;    
    }else{
        return fb2(n-1,res2,res1 + res2);
    }

3.方法3:迭代


function fb3(n){
    var res1 = 1;
    var res2 = 1;
    var sum = res2;
    for(var i = 2;i < n;i ++){
        sum = res1 + res2;
        res1 = res2;
        res2 = sum;
    }
    return sum;

4.方法4:闭包


const fb4 = function(){
    var mem = [0,1];
    var f = function(n){
        var res = mem[n];
        if(typeof res !== 'number'){
            mem[n] = f(n-1) + f(n-2);
            res = mem[n];
        }
        return res;
    }
    return f;
}();

18.HTTP与HTTPS的区别&HTTPS的工作原理

原文

19.for in和for of

forEach介绍

objArr.forEach(function (value) {
  console.log(value);
});

foreach 方法没办法使用 break 语句跳出循环,或者使用return从函数体内返回

for-in介绍

for(var index in objArr){
    console.log(objArr[index])
}

以上代码会出现的问题:

  1. index 值 会是字符串(String)类型
  2. 循环不仅会遍历数组元素,还会遍历任意其他自定义添加的属性,如,objArr上面包含自定义属性,objArr.name,那这次循环中也会出现此name属性
  3. 某些情况下,上述代码会以随机顺序循环数组
  4. for-in循环设计之初,是给普通以字符串的值为key的对象使用的。而非数组。

for-of介绍

for(let value of objArr){
    console.log(value)
}
  1. 可以避免所有 for-in 循环的陷阱
  2. 不同于 forEach(),可以使用 break, continue 和 return
  3. for-of 循环不仅仅支持数组的遍历。同样适用于很多类似数组的对象
  4. 它也支持字符串的遍历
  5. for-of 并不适用于处理原有的原生对象

for-of 遍历 Set

var uniqueWords = new Set(words);

for (var word of uniqueWords) {
  console.log(word);
}

for-of 遍历 Map

for (var [key, value] of phoneBookMap) {
  console.log(key + "'s phone number is: " + value);
}

for-of 遍历原生对象

// 输出对象自身可以枚举的值
for (var key of Object.keys(someObject)) {
  console.log(key + ": " + someObject[key]);
}

20.对Url参数解析

// serialize data  e.g. {a:"abc",b:"123"} -> "a=abc&b=123"
export function serialize(data: any, isTraditional: boolean = false): string {
  let arr = [];
  if (typeof data == "object") {
    for (let key in data) {
      if (data[key] != null) {
        let item = data[key];
        if (isTraditional && item instanceof Array) {
          arr.push(item.map(function (field) {
            return encodeURIComponent(key) + "=" + encodeURIComponent(field);
          }).join("&"));
        } else {
          arr.push(encodeURIComponent(key) + "=" + encodeURIComponent(item));
        }
      }
    }
    return arr.join("&");
  }
  return '';
}
/**
* reverse serialize data  e.g. "a=abc&b=123" -> {"a":"abc","b":"123"}
* @param {string} str
* @param {boolean} isTraditional if true e.g. "a=abc&b=123&b=456" -> {"a":"abc","b":["123","456"]}
*/
export function reSerialize(str: string, isTraditional = true): object {
  let s = decodeURIComponent(str);

  return s.split("&").reduce(function (prev:any, cur) {
    let flag = cur.indexOf("=");
    let key = cur.slice(0, flag);
    let val = cur.slice(flag + 1);

    if (isTraditional) {
      if (prev[key]) {
        prev[key] instanceof Array ? prev[key].push(val) : prev[key] = [prev[key], val];
      } else {
        prev[key] = val;
      }
    } else {
      prev[key] = val;
    }

    return prev;
  }, {});
}


21.判断一个对象是否为空

1.最常见的思路,for...in...遍历属性,为真则为“非空数组”;否则为“空数组”


function objNull (obj) {
	for(var arr in obj) {
		return false
	}
	return true
}

2.通过JSON自带的.stringify方法来判断:

if(JSON.stringify(c) == '{}') {
	return true
}

3.ES6新增的方法Object.keys():

if(Object.keys(obj).length == 0) {
	return true
}else {
	return false
}

22.判断数据类型

// 判断类型 Object.prototype.toString.call();
function isType(type){ // type  == 'boolean'
  return function (obj){
    return Object.prototype.toString.call(obj).includes(type);
  }
}
// 包装成一个高阶函数 批量生成函数  
let types = ['String','Object','Array','Null','Undefined','Boolean'];
let fns = {};
types.forEach(type=>{ // 批量生成方法
    fns['is'+type] = isType(type)
})
let a = true;
console.log(fns.isString(a)); // 函数柯里化 // 偏函数
// 高阶函数
// lodash after 

23.观察者模式

// 被观察者  我家小宝宝 心情好不好 心情好 -》 心情不好
class Subject {
    constructor(name){
        this.name = name;
        this.observers = []; //观察者 要存放到被观察者中
        this.state = '心情很美丽'
    }
    // 被观察者要提供一个接受观察者的方法
    attach(observer){
        this.observers.push(observer); // 存放所有的观察者
    }
    setState(newState){ // 更改被观察者的状态
        this.state = newState;
        this.observers.forEach(o=>o.update(newState));
    }
} 
class Observer{
    constructor(name){
        this.name = name;
    }
    update(newState){ // 用来通知所有的观察者状态更新了
        console.log(this.name+'说:宝宝'+newState);
    }
}
let sub = new Subject('小宝宝');
let o1 = new Observer('爸爸');
let o2 = new Observer('妈妈');
sub.attach(o1);
sub.attach(o2);
sub.setState('心情不好了');
sub.setState('心情不好了1')

24.手写promise

class Promise {
  constructor(executor) {
    // 默认状态是等待态
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    // 存放成功的回调
    this.onResolvedCallbacks = [];
    // 存放失败的回调
    this.onRejectedCallbacks = [];
    let resolve = (data) => {
      if (this.status === 'pending') {
        this.value = data;
        this.status = 'resolved';
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    }
    let reject = (reason) => {
      if (this.status === 'pending') {
        this.reason = reason;
        this.status = 'rejected';
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    }
    try { // 执行时可能会发生异常
      executor(resolve, reject);
    } catch (e) {
      reject(e); // promise失败了
    }
  }
  then(onFulFilled, onRejected) {
    if (this.status === 'resolved') {
      onFulFilled(this.value);
    }
    if (this.status === 'rejected') {
      onRejected(this.reason);
    }
    // 当前既没有完成 也没有失败
    if (this.status === 'pending') {
      // 存放成功的回调
      this.onResolvedCallbacks.push(() => {
        onFulFilled(this.value);
      });
      // 存放失败的回调
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}
module.exports = Promise;
// 写完promise会测试一下

25.call、apply 及 bind 函数

call

  • 不传入第一个参数,那么上下文默认为 window
  • 改变了 this 指向,让新的对象可以执行该函数,并能接受参数
Function.prototype.myCall = function(context='window') {
	if(typeof this !== 'function') {
		throw new TypeError('Error')
	}

	context.fn = this;
	const args = [...arguments].slice(1);
	let result = context.fn(...args);
	delete context.fn;
	return result
	
}

apply

  • apply 的实现也类似,区别在于对参数的处理
Function.prototype.myApply = function (context='window') {
	if(typeof this !== 'function') {
		throw new TypeError('Error')
	}
	context.fn = this;
	let result;

	if(arguments[1]){
		result = context.fn(...arguments[1]);
	}else {
		result = context.fn();
	}
	delete context.fn;

	return result;
}

bind

  • bind 返回了一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过 new 的方式,我们先来说直接调用的方式
  • 对于直接调用来说,这里选择了 apply 的方式实现,但是对于参数需要注意以下情况:因为 bind 可以实现类似这样的代码 f.bind(obj, 1)(2),所以我们需要将两边的参数拼接起来,于是就有了这样的实现 args.concat(...arguments)
  • 最后来说通过 new 的方式,在之前的章节中我们学习过如何判断 this,对于 new 的情况来说,不会被任何方式改变 this,所以对于这种情况我们需要忽略传入的 this
Function.prototype.myBind = function(context) {
	if(typeof this !== 'function') {
		throw new TypeError('Error')
	}
	const _this = this;
	const args = [...arguments].slice(1);

	return function F() {
		if(this instanceof F) {
			return new _this(...args,...arguments);
		}

		return _this.apply(context, args.concat(...arguments))
	}
}

26. setState可能是同步的

  • setState 在react里的合成事件和钩子函数中是“异步”的。

  • setState 在原生事件和 setTimeout 中是同步的。

27. JS数组常用数组方法实现原理

map的用法和实现原理

用法
"map"即"映射",也就是原数组被"映射"成对应新数组。

let arr = [1,2,3];
arr1 = arr.map(item => item * 2)

实现原理

Array.prototype._map = function (callback) {
let newArr = [];
for (let i = 0; i < this.length; i++) {
  newArr.push(callback && callback(this[i]))
}
return newArr
}
let result = [1,2,3]._map(item => item + 1)
console.log(result);  //[2, 3, 4]

filter的用法和实现原理

Array.prototype._filter = function (callback) {
let newArr = [];
for (let i = 0; i < this.length; i++) {
  callback && callback(this[i], i) && newArr.push(this[i])
}
return newArr
}
let filter = [1,2,3]._filter(item => item > 1) 
console.log(filter); // [2,3]

find的用法和实现原理

Array.prototype._find = function (callback) {
    let currentVal
    for (let i = 0; i <this.length; i++) {
      if (callback(this[i])) {
        currentVal = this[i]
        break
      }
    }
    return currentVal
}
let result = [1,2,3,4]._find(item => item > 1)
console.log(result);  // 2

reduce的用法和实现原理

用法
累加
let arr=[2,4,6,8];
let arr1=arr.reduce((prev, item) => prev + item, 0)
console.log(arr1) // 20
复制代码实现原理
Array.prototype._reduce = function (callback, initVal) {
    for (let i = 0; i < this.length; i++) {
      initVal = callback(initVal, this[i], i, this)
    }
    return initVal
}
let result = [1,2,3,4]._reduce((prev, item) => prev + item, 0)
console.log(result);  // 10

some的用法和实现原理

用法
只要有一个元素满足条件就返回true
let arr = [1,2,3,4];
let res = arr.some(item => item > 4)
console.log(res);   // false
复制代码实现原理
Array.prototype._some = function (callback) {
// callback &&
let result = false
for (let i = 0; i < this.length; i++) {
  result = callback && callback(this[i])
}
return result
}
let result = [1,2,3]._some(item => item > 1)
console.log(result);  // true

every的用法和实现原理

用法
数组中每一个元素满足条件就返回true
let arr = [1,2,3,4];
let res = arr.every(item => item > 0)
console.log(res);   // true
复制代码实现原理
Array.prototype._every = function (callback) {
let result = true
for (let i = 0; i < this.length; i++) {
  if(!callback(this[i])) {
    result = false
    break
  }
}
return result
}
let result = [1,2,3]._every(item => item > 0)
console.log(result);  //true
let result = [1,2,3]._every(item => item > 1)
console.log(result);  //false

reduce实现filter,map 数组扁平化等

reduce实现map

Array.prototype._map = function (callback) {
  if(typeof callback === 'function') {
    return this.reduce((prev,item,index,arr) => {
      prev.push(callback(item, index, arr))
      return prev
    }, [])
  } else {
    console.log(new Error('callback is not function'))
  }
}

let val = [1, 5, 6]._map(item => item+ 1)
console.log(val);  // [2, 6, 7]

reduce实现filter

  Array.prototype._filter = function (callback) {
    if(typeof callback === 'function') {
      return this.reduce((prev,item,index,arr) => {
        callback(item, index, arr) ? prev.push(item) : null
        return prev
      }, [])
    } else {
      console.log(new Error('callback is not function'))
    }
  }
  let val = [1, 5, 6]._filter(item => item > 2)
  console.log(val);  // [5, 6]

求最大值/最小值

  let arr = [1, 2, 3, 4, 5]

console.log(arr.reduce((prev, cur) => Math.max(prev, cur))); // 5

console.log(arr.reduce((prev, cur) => Math.min(prev, cur))); // 1

数组去重

let arr = [1, 2, 3, 1, 1, 2, 3, 3, 4, 3, 4, 5]

let result = arr.reduce((prev, item, index, arr) => {
!prev.includes(item) && prev.push(item);
return prev
}, [])
console.log(result);  //[1, 2, 3, 4, 5]

数组扁平化

let arr = [1, 2, '3js', [4, 5, [6], [7, 8, [9, 10, 11], null, 'abc'], {age: 58}, [13, 14]], '[]', null];
function f(arr) {
    if(Array.isArray(arr)) {
      return arr.reduce((prev, item) => {
        return Array.isArray(item) ? prev.concat(f(item)) : prev.concat(item)
      }, [])
    } else {
      throw new Error("arr + ' is not array'")
    }
}

28.js数据结构-二叉树(二叉搜索树)

https://segmentfault.com/a/1190000017798199?utm_source=tag-newest

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值