Git Product home page Git Product logo

blogs's People

Contributors

wangbow avatar

Stargazers

 avatar

blogs's Issues

卡片图片的动态收缩渲染

<!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>卡片动态展示</title>
</head>
<style>
    * {
        margin: 0;
        padding: 0;
    }

    ul,
    li {
        list-style: none;
    }

    ul {
        width: 1260px;
        margin: 0 auto;
    }

    ul li {
        float: left;
        margin-right: 20px
    }

    ul li:nth-child(4) {
        margin-right: 0
    }

    .container {
        position: relative;
        width: 300px;
        height: 400px;
        overflow: hidden;
    }

    .img-wrapper {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 300px;
        overflow: hidden;
    }

    .img-wrapper img {
        width: 100%;
        height: 100%;
        transition: all .5s;
    }

    .info {
        position: absolute;
        width: 100%;
        height: 300px;
        bottom: -200px;
        background-color: rgb(247, 242, 242);
        transition: all .5s;
    }

    .info h3 {
        color: rgb(21, 74, 172);
        line-height: 60px;
        text-align: center;
    }

    .info p {
        text-align: center;
        font-family: 'fangsong';
        font-size: 16px;
        font-weight: 700;
        line-height: 20px;
    }

    .container:hover .info {
        bottom: 0px;
    }

    .container:hover .img-wrapper img {
        transform: scale(1.2);
        /* 设置图片的高斯模糊效果 */
        filter: blur(1px);
    }
</style>

<body>
    <div>
        <ul>
            <li>
                <div class='container'>
                    <div class='img-wrapper'>
                        <img src='./images/aa.png' />
                    </div>
                    <div class="info">
                        <h3>The Beautiful location</h3>
                        <p>this is test</p>
                        <p>foot is here</p>
                    </div>
                </div>
            </li>

            <li>
                <div class='container'>
                    <div class='img-wrapper'>
                        <img src='./images/bb.png' />
                    </div>
                    <div class="info">
                        <h3>The Beautiful location</h3>
                        <p>this is test</p>
                        <p>foot is here</p>
                    </div>
                </div>
            </li>

            <li>
                <div class='container'>
                    <div class='img-wrapper'>
                        <img src='./images/cc.png' />
                    </div>
                    <div class="info">
                        <h3>The Beautiful location</h3>
                        <p>this is test</p>
                        <p>foot is here</p>
                    </div>
                </div>
            </li>

            <li>
                <div class='container'>
                    <div class='img-wrapper'>
                        <img src='./images/dd.png' />
                    </div>
                    <div class="info">
                        <h3>The Beautiful location</h3>
                        <p>this is test</p>
                        <p>foot is here</p>
                    </div>
                </div>
            </li>

        </ul>
    </div>
</body>

</html>

卡片初始化
image
卡片hover之后
image

模块化的发展(-)

一、为什么会有模块化

  1. 当一个项目开发的越来越复杂的时候,会遇到一些问题,比如:
    命名冲突:当项目由团队进行协作开发的时候,不同开发人员的变量和函数命名可能相同;即使是一个开发,当开发周期比较长的时候,也有可能会忘记之前使用了什么变量,从而导致重复命名,导致命名冲突。
    文件依赖:代码重用时,引入js文件的数目可能少了,或者引入的顺序不对,比如使用boostrap的时候,需要引入jQuery,并且jQuery的文件必须要比boostrap的js文件先引入。

  2. 当使用模块化开发的时候可以避免以上的问题,并且让开发的效率变高,以及方便后期的维护

二、模块化开发的演变过程

  1. 全局函数
        function add(a, b) {
            return a + b;
        }

        function jian(a, b) {
            return a - b;
        }

        function mul(a, b) {
            return a * b
        }

        function divide(a, b) {
            return a / b
        }

存在的问题:
1.污染了全局变量,无法保证不与其他模块发生变量名冲突。
2.模块成员之间看不出直接关系。

  1. 对象封装
var cal = {
            add: function (a, b) {
                return a + b;
            },
            
            jian: function (a, b) {
                return a - b;
            },

            mul: function (a, b) {
                return a * b
            },

            divide: function (a, b) {
                return a / b
            }
        }

存在的问题:
1.暴露了所有的模块成员,内部状态可以被外部改写,不安全
2.命名空间嵌套,会越来越长

  1. 私有公有成员分离
 var cals = (function () {
            //私有的成员变量
            var convert = function (data) {
                return parseInt(data)
            }
            //提供公共方法,外部可以调用
            var add = function (a, b) {
                return convert(a) + convert(b);
            }

            var jian = function (a, b) {
                return convert(a) - convert(b)
            }

            var mul = function (a, b) {
                return convert(a) * convert(b)
            }

            var divide = function (a, b) {
                return convert(a) / convert(b)
            }

            return {
                add: add,
                jian: jian,
                mul: mul,
                divide: divide
            }
        })()

好处:
1.利用此种方式将函数包装成一个独立的作用域,私有空间的变量和函数不会影响到全局作用域。
2.以返回值的方式得到模块的公共成员,公开公有方法,隐藏私有空间内部的属性、元素,比如注册方法中可能会记录日志。
3.可以有选择的对外暴露自身成员。
4.某种意义上来说,解决了变量命名冲突的问题。

  1. 模块的扩展与维护
// 计算模块
(function (calculator) {
    function convert(input) {
        return parseInt(input);
    }
    calculator.add = function(a, b) {
        return convert(a) + convert(b);
    }
    window.calculator = calculator;
})(window.calculator || {});

// 新增需求
(function (calculator) {
    calculator.mul = function (a , b) {
        return a * b;
    }
    window.calculator = calculator;
})(window.calculator || {});

1.利用此种方式,有利于对庞大的模块的子模块划分
2.实现了开闭原则:对新增开发,对修改关闭。对于已有文件尽量不要修改,通过添加新文件的方式添加新功能。

JS-基本类型数据的数组去重方式

JS数组-对基本类型的数据进行去重

通过Set去重

  const arr = [1,2,3,1,2,3, 'true', 'true', true, true, 4,4];
  const setFunction1 = arr => [...new Set(arr)];
  const setFunction2 = arr => Array.from(new Set(arr));

通过includes去重复

  const arr = [1,2,3,1,2,3, 'true', 'true', true, true, 4,4];
  const includesArrFunction = (arr) => {
            const newArr = [];
            for (let i = 0; i < arr.length; i++) {
                if (!newArr.includes(arr[i])) {
                    newArr.push(arr[i])
                }
            }
            return newArr;
        }

通过indexOf去重

  const arr = [1,2,3,1,2,3, 'true', 'true', true, true, 4,4];
  const idnexOfArrFunction = (arr) => {
            const idnexOfArr = [];
            for (let i = 0; i < arr.length; i++) {
                if (idnexOfArr.indexOf(arr[i]) === -1) {
                    idnexOfArr.push(arr[i])
                }
            }
            return idnexOfArr;
        }

双重for循环,内层循环进行比较,注意这个splice方法会修改原数组,这里建议吧arr 深拷贝一份

  const arr = [1,2,3,1,2,3, 'true', 'true', true, true, 4,4];
  const xunhuanFunction = (arr) => {
            for (let i = 0; i < arr.length; i++) {
                for (let j = i + 1; j < arr.length; j++) {
                    if (arr[i] === arr[j]) {
                        arr.splice(j, 1);
                        j--;
                    }
                }
            }
        }

filter 原始数组中元素的索引等于当前索引值时返回,否则返回当前元素

  const arr = [1,2,3,1,2,3, 'true', 'true', true, true, 4,4];
  const filterArrFunction = (arr) => {
            return arr.filter((item, index, array) => {
                return array.indexOf(item) === index;
            })

        }

JS-基本类型数据进行数组扁平化

数组扁平化是指将一个多维数组变为一维数组。

flat()方法创建一个新数组,其中所有子数组元素都以递归方式连接到该数组中,直到达到指定的深度为止 ,参数指定深度,也可以是无穷大

 const arr = [1, 2, [3, 4, [5, 6]]];
 const newArr = arr.flat(2);
 const newArr2 = arr.flat(Infinity);

es6的扩展运算符能将二维数组变为一维,若arr中含有数组则使用一次扩展运算符,直至没有为止。

 const arr = [1, 2, [3, 4, [5, 6]]];
 function flatten(arr) {
            while (arr.some(item => Array.isArray(item))) {
                arr = [].concat(...arr);
            }
            return arr;
  }

递归的遍历每一项,当为数组时则继续,不为数组则concat

  const arr = [1, 2, [3, 4, [5, 6]]];
  function flatten(arr) {
            var result = [];
            for (let i = 0, len = arr.length; i < len; i++) {
                if (Array.isArray(arr[i])) {
                    result = result.concat(flatten(arr[i]))
                } else {
                    result.push(arr[i])
                }
            }
            return result;
        }

reduce方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

 const arr = [1, 2, [3, 4, [5, 6]]];
 const reduceArr = (arr) => {
            return arr.reduce((result, item) => {
                return result.concat(Array.isArray(item) ? reduceArr(item) : item);
            }, [])
        }

不可不知的20个云计算术语和定义

本文将解释与云计算有关的几个术语和定义

1.基础设施即服务(IaaS):

IaaS是云计算的一种模式,通过互联网将数据存储、网络和CPU能力等虚拟化资源作为一项服务提供给用户。用户可以访问他们的虚拟机,还可以在服务器上自行启动、停止和修改服务.

2.平台即服务(PaaS):

PaaS是云计算的一种模式,资源由供应商(第三方提供商)提供给用户。软硬件托管在PaaS基础设施上。使用这种云计算,用户按用户数量付费。

3.软件即服务(SaaS):

软件即服务是云计算的一种模式,资源由供应商提供给用户,可通过网络来提供,用户可以访问软件和数据库。

4.私有云(专有云):

私有云是只有一小部分用户才能访问的云基础设施。它提供给一家组织而不是普通公众使用。私有云提供了灵活性、可扩展性、自助服务和许多可定制的功能。

5.公有云:

与私有云相反,公有云是所有用户都可以通过互联网来访问的云基础设施。公有云还提供了灵活性、可扩展性和按使用付费。如果公有云使用正确的安全方法来正确实施,它可以像私有云一样安全。

6.托管云:

托管云或托管服务提供商是一家IT服务提供商,允许客户部署基于云的服务。说到云基础服务,我们可能提及IaaS、PaaS、SaaS、网络以及应用程序和系统服务。这些服务通过网络提供给客户。

7.混合云:

这种类型的云结合了公共解决方案、私有解决方案和本地解决方案。混合云是一种解决方案,主要目标是创建一个自动化、灵活、可扩展的环境,其成本比公有云和私有云要低。混合云集公有云和私有云的优点于一身。

8.多云:

这种类型的云由多家云服务提供商组成。多云结合使用公有云、私有云或同时提供这两者的云(名为混合云)。多云适用于用户想要某家云提供商提供特定的服务(比如公有云),又想要不同的云提供商提供数据存储服务。

9.主机:

主机是托管多个虚拟专用服务器机器的物理硬件。每个主机都包括处理器、内存、网络连接、硬驱和操作系统(OS)。这些部件的结合将使程序和应用程序可以为客户顺畅地运行。

10.**处理器单元(CPU):

通过服务器上运行的程序和应用程序执行指令的电子芯片名为**处理器单元,简称CPU。CPU的代数和功率以及核心数量对于应用程序的性能来说最重要。在云计算中,您会常常看到vCPU这个词,其意思是虚拟**处理器单元。

11.随机存取存储器(RAM):

随机存取存储器负责临时存储服务器上运行的信息。存储在RAM中的信息可以从服务器上的任何地方非常快速地访问,CPU不必每次在需要信息时都搜索硬驱上的信息。RAM的速度比HDD或SSD快得多。

12.硬驱:

硬驱负责存储数据。服务器上的硬驱可能是SSD或NVMe。NVMe的速度比SSD快得多,比较网站加载时间方面的测试就可以发现差异。

13.可扩展性和弹性:

可扩展性是云计算的一个很大的优势,可以处理不断增加的工作负载。它可以根据应用程序的需求来增加资源,对用户而言可能是月底成本效率的一个主要因素。弹性也可以使配置的资源尽可能匹配当前的需求。

14.负载均衡:

负载均衡是在一台服务器无法处理所有入站流量时,将Web请求分发到多台服务器的功能。对于每分钟有数千个请求的非常庞大的应用程序而言,负载均衡系统越多越好。

15.复制:

复制是跨云基础设施(比如服务器或数据存储系统)共享资源的过程,以提高应用程序、数据库系统、数据或系统的可用性和可靠性。

16.快照:

快照是实时环境的即时副本,万一将来应用程序出岔子,可用于恢复。此外,向生产环境发布新内容之前,拍摄快照以用于测试开发目的。有了快照,您可以轻松地将数据立即恢复到环境中。

17.数据库:

数据库是在单独的表中组织的数据集合,以便更好地查询和访问。

18.数据库迁移:

在不同的存储系统或服务器之间移动数据的过程名为数据库迁移。

19.内容分发网络(CDN):

内容分发网络安全保护提供了一系列广泛的机制,以防范对网站实施的DDoS、黑客攻击或垃圾邮件攻击。此外,CDN可以缓解网络连接速度慢的问题。

20.云市场:

云市场是云托管提供商开展运营时配套的在线市场。客户可以根据自己的需要,订阅和订购应用程序。

export ,export default 和 import 区别 以及用法

export ,export default 和 import 区别 以及用法

ES6模块主要有2个功能:export 和 import
export用于对外导出变量,函数,对象等模块的接口
import用于在一个模块中引入export导出的变量,函数,对象等

export 和 import

export 和 import 单个变量
a.js
export const name = "benben"
b.js

import {name} from "./a";
console.log(name); //benben

export 和 import 多个变量
a.js

export const name1 = "benben1";
export const name2 = "benben2";

b.js

import {name1,name2} from "./a";
console.log(name1,":", name2); //benben1:benben2

函数也是同理;

export 和 export default区别:

1.export和export default均可用于导出常量,函数,文件,模块等
2.可以通过import +(常量,函数,文件,模块)名的方式,将其导入,以便对其使用
3.在一个文件或模块中,export,import可以有多个, export default仅有一个
4.通过export方式导出,在导入时要加{ },export default则不需要

leetCode 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
var lengthOfLongestSubstring = function(s) {
    let arr = [], max=0;
    for(let i=0;i<s.length;i++){
        const index = arr.indexOf(s[i]);
        if(index!=-1){
            arr.splice(0,index+1);
        }
        arr.push(s[i]);
        max = Math.max(arr.length,max);
    }
    return max;
};

var s = 'pwwkew';
lengthOfLongestSubstring(s);

代理模式-保护代理和虚拟代理

定义

为一个对象提供一个代用品或占位符,以便控制对它的访问

核心

当客户不方便直接访问一个 对象或者不满足需要的时候,提供一个替身对象 来控制对这个对象的访问,客户实际上访问的是 替身对象。

替身对象对请求做出一些处理之后, 再把请求转交给本体对象

代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一 些额外的事情

实现

代理模式主要有三种:保护代理、虚拟代理、缓存代理

保护代理主要实现了访问主体的限制行为,以过滤字符作为简单的例子

  function message(message) {
            console.log(message)
        }

        function proxyMessage(info) {
            if (!info) {
                return;
            }

            info = info.replace(/操/g, '')

            message(info);
        }

        proxyMessage("操,贱人");

它的意图很明显,在访问主体之前进行控制,没有消息的时候直接在代理中返回了,拒绝访问主体,这数据保护代理的形式

虚拟代理在控制对主体的访问时,加入了一些额外的操作
在滚动事件触发的时候,也许不需要频繁触发,我们可以引入函数节流,这是一种虚拟代理的实现

 function debounce(fn, delay) {
            delay = delay || 20;
            var time = null;
            return function () {
                var arg = arguments;
                clearTimeout(time);
                // time = null;
                time = setTimeout(function () {
                    fn.apply(this, arg);
                }, delay);
            }
        }

        var count = 0;
        // 主体
        function scrollHandle(e) {
            console.log(e.type, ++count); // scroll
        }

        // 代理
        var proxyScrollHandle = (function () {
            return debounce(scrollHandle, 500);
        })();

        window.onscroll = proxyScrollHandle;

实现一个最简单的单例模式

单例模式(Singleton Pattern)是 设计模式中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。

  function Person(name) {
            this.name = name;
   }
    const singleton = (function () {
           let instance = null;
            return {
                getInstance: function () {
                    if (!instance) {
                        instance = new Person("aaa");
                    }
                    return instance;
                }
            }
        })();

        let s1 = singleton.getInstance();
        let s2 = singleton.getInstance();
        console.log(s1 == s2); //true
        console.log(s1.name); //aaa

浅谈强缓存和协商缓存

缓存分为-强缓存 和 协商缓存

强缓存

主要通过http请求的response hearder 中的cache-control (http1.1)和 expire(http1.0) 俩个字段来控制
cache-control:值可以为"public", "private", "max-age=xxx", 一般用xxx秒内再次访问这个资源,均使用本地缓存来处理,不在向服务器发起请求
问题点:当发布新版本的时候,后台接口也同步更新了,那就gg了。有缓存的用户还在使用旧接口,而那个接口已经被后台干掉了。怎么办?

协商缓存

协商缓存最大的问题是:每次请求都要去服务器进行缓存有效性的验证

最佳实践:

缓存的意义在于,更多的使用本地资源,提高用户体验,降低服务器的压力。**
所以: 最佳实践就是尽可能的命中强缓存,同时在更新版本的时候,让客户端的缓存失效

在更新版本之后,如何让用户第一时间使用最新的资源文件呢?机智的前端们想出了一个方法,在更新版本的时候,顺便把静态资源的路径改了,这样,就相当于第一次访问这些资源,就不会存在缓存的问题

webpack打包文件的时候,使用了chunkhash

综上所述,我们可以得出一个较为合理的缓存方案:

HTML:使用协商缓存。
CSS&JS&图片:使用强缓存,文件命名带上hash值。

深拷贝和浅拷贝区别

深拷贝和浅拷贝

变量存储类型

在理解拷贝之前我们要先熟悉变量存储类型 变量的数据类型分为 基本数据类型 (值类型)和 复杂数据类型(引用数据类型)
基本数据类型的值是直接存放在栈内存的 而复杂数据类型的栈内存保存的是内存地址 值都是保存在堆内存中

浅拷贝

概念
我们可以理解为 浅拷贝就是创建一个新对象 这个对象有着被拷贝对象的对象属性值
如果是基本数据类型 拷贝的就是这个基本数据类型的值 改变新对象不会导致原来的数据发生改变
如果属性是复杂数据类型 拷贝的就是复杂数据类型的内存地址
所以如果其中一个对象改变了这个地址 就会影响到另外一个对象 就会导致原数据发生改变
这里借用GitHub中ConardLi大佬的图片来方便我们理解
image

浅拷贝使用实现:

方法一 Object.assign()

语法 Object.assign(target, … sources)
第一个参数是目标对象
第二分参数是原对象(也就是被拷贝的对象)
这是es6提供给我们的新方法 我们使用的时候需要注意兼容性的问题
还有个需要注意的是 这个方法一般用来浅拷贝对象 用来处理数组的时候 会把数组视为对象

const obj = {
    name:'xiaoming',
    age:18,
    hobby:['football','music']
}

const obj2 = Object.assign({}, obj)
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);

image

方法2 利用展开运算符进行浅拷贝

const obj = {
    name:'xiaoming',
    age:18,
    hobby:['football','music']
}

const obj2 = {...obj};
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);

image
方法3 forin循环

const obj = {
    name:'xiaoming',
    age:18,
    hobby:['football','music']
}

const obj2 = {};
for(const key in obj){
    obj2[key] = obj[key]
}
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);

image

深拷贝

概念

深拷贝指的是 将被拷贝的对象从内存中完全拷贝一份出来 在堆内存中开辟一个新的区域来存放新对象 并且修改新对象不会影响原来对象
我们在借用一次 GitHub中ConardLi大佬的图片来方便我们理解
image

方法一:递归

const obj = {
    name:'xiaoming',
    age:18,
    hobby:['football','music']
}

function deepClone(data){
    if(!data ){
        return data;
    }
    const target = Array.isArray(data)? [] :{};
    for(const key in data){
        const item = data[key];
        if(typeof item === 'function'){
          target[key] = new Function(`return ${item.toString()}`)(); 
        }else if(typeof item === 'object'){
            target[key] = deepClone(item);
        }else{
             target[key] = item
        }
    }

    return target;
}

const obj2 = deepClone(obj);
obj2.hobby.push('swim');
console.log('obj',obj);
console.log('obj2',obj2);

image

方法二 JSON.parse(JSON.stringify())

乍看 JSON.parse(JSON.stringify()) 不太明白 我们来解释一下
JSON.parse() 是一个生成新对象的方法
JSON.stringify() 是把原对象序列化为一个JSON字符串
简单的说 就是先把源对象序列化为一个JSON字符串 然后利用JSON.parse()方法转变为一个新对象
这也是我们最常用的方法
我们来使用看看结果如何:

const obj = {
    name:'xiaoming',
    age:18,
    hobby:['football','music']
};

const objString = JSON.stringify(obj);
const obj2 = JSON.parse(objString);
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);

image

有以下几种情况时,不能正确的进行深拷贝:
1.obj里面有new Date(),深拷贝后,时间会变成字符串的形式。而不是时间对象;

let a = {
     name: 'a',
     date: [new Date(1536627600000), new Date(1540047600000)],
   };

   let b = JSON.parse(JSON.stringify(a))
console.log(a,b);

image

2.obj里有RegExp、Error对象,则序列化的结果会变成空对象{};

const a = {
     name: 'a',
     date: new RegExp('\\w+'),
   };
 const b = JSON.parse(JSON.stringify(a));
 a.name = 'test'
 console.log( a, b)

image

3.obj里有function,undefined,则序列化的结果会把function或 undefined丢失

  const a = {
        name: 'a',
        date: function hehe() {
          console.log('fff')
        },
      };
      const b = JSON.parse(JSON.stringify(a));
      a.name = 'test'
      console.log(a, b)

image

4.obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null;

   const a = {
        name: 'a',
        date: NaN,
      };
      const b = JSON.parse(JSON.stringify(a));
      a.name = 'test'
      console.log(a, b)

image

函数限流实现

函数限流解释: 指定时间大量重复触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效

思路: 我们可以设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活(类似于技能冷却时间)。

实现: 这里借助 setTimeout来做一个简单的实现,加上一个状态位 flag来表示当前函数是否处于工作状态.

HTML CODE

<input type = "text" id = "root" />

**JS CODE **

        function throttle(fn) {
            let flag = true
            return function () {
                if (!flag) {
                  return false
                }
                
                flag = false
                setTimeout(() => {
                    fn() 
                    flag = true;
                },1000)
            }
        }

        function sayOk(){
            console.log("throttle success");
        }

        const ele = document.getElementById("root")
        ele.addEventListener("input" , throttle(sayOk))

LeeCode - 变位词组

** 编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。**

输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/group-anagrams-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
  • 所有输入均为小写字母。
  • 不考虑答案输出的顺序。
    var strs = ["eat", "tea", "tan", "ate", "nat", "bat"];
        let groupAnagrams = function (strs) {
            let a, j, t = 0, result = [], map = new Map();
            for (let b of strs) {
                // 吧数组的每个字符串转换成数组,并进行排序,在转回为字符串
                a = b.split("").sort().join("");
                // 获取每个字符串数组对应的索引
                map.has(a) ? (j = map.get(a)): map.set(a, j = t++);
                // 放入result 数组的不同位置
                (result[j] = result[j] ||[]).push(b);
            }

            return result;
        };
 

实现一个JS中的call

Call的特点

  • 1.改变当前函数的this指向

  • 2.还会让当前函数执行

  Function.prototype.mycall = function (context) {
            //通过Object对基础数据类型,进行包装成对象,如果是null || undefined, context就是全局的window对象
            context = context ? Object(context) : window;
           // 吧当前的函数挂载到context对象的fn属性上
            context.fn = this;
            const args = [];
            //通过args来对参数进行存储
            for(let i =1; i<arguments.length;i++){
                args.push(`arguments[${i}]`);
            }
            //通过eval执行字符串
            const result = eval(`context.fn(${args})`);
            //执行完毕,删除context.fn
            delete context.fn;
            return result;
        }

    function fn1() {
            console.log(this, arguments);
     }

   fn1.mycall({ name: "aaa" }, '1', '2');
   fn1.mycall('hello','ww','wqq');
function fn2(){
    console.log(this,2);
}

多个call执行,会让call方法中的this改变成fn2这个参数
fn1.mycall.mycall(fn2) ;

写好 JS 条件语句的 5 条守则

写好 JS 条件语句的 5 条守则

在用 JavaScript 工作时,我们经常和条件语句打交道,这里有5条让你写出更好/干净的条件语句的建议。

  • 多重判断时使用 Array.includes
  • 更少的嵌套,尽早 return
  • 使用默认参数和解构
  • 倾向于遍历对象而不是 Switch 语句
  • 对 所有/部分 判断使用 Array.every & Array.some

多重判断时使用 Array.includes
让我们看一下下面这个例子:

`
function test(fruit) {

if (fruit == 'apple' || fruit == 'strawberry') {
console.log('red');
}
}
`
第一眼,上面这个例子看起来没问题。如果我们有更多名字叫 cherry 和 cranberries 的红色水果呢?我们准备用更多的 || 来拓展条件语句吗?

我们可以用 Array.includes (Array.includes)重写条件语句。

`
function test(fruit) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

if (redFruits.includes(fruit)) {
console.log('red');
}
}
`

我们把红色的水果(red fruits)这一判断条件提取到一个数组。这样一来,代码看起来更整洁。

**** 更少的嵌套,尽早 Return

让我们拓展上一个例子让它包含两个条件。

  • 如果没有传入参数 fruit,抛出错误
  • 接受 quantity 参数,并且在 quantity 大于 10 时打印出来

`
function test(fruit, quantity) {

const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

// 条件 1: fruit 必须有值
if (fruit) {
// 条件 2: 必须是red的
if (redFruits.includes(fruit)) {
console.log('red');

  // 条件 3: quantity大于10
  if (quantity > 10) {
    console.log('big quantity');
  }
}

} else {
throw new Error('No fruit!');
}
}

// 测试结果
test(null); // error: No fruits
test('apple'); // print: red
test('apple', 20); // print: red, big quantity
`

在上面的代码, 我们有:

  • 1个 if/else 语句筛选出无效的语句
  • 3层if嵌套语句 (条件 1, 2 & 3)

我个人遵循的规则一般是在发现无效条件时,尽早Return。

`
/_ 当发现无效语句时,尽早Return _/

function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

// 条件 1: 尽早抛出错误
if (!fruit) throw new Error('No fruit!');

// 条件 2: 必须是红色的
if (redFruits.includes(fruit)) {
console.log('red');

// 条件 3: 必须是大质量的
if (quantity > 10) {
  console.log('big quantity');
}

}
}
`
这样一来,我们少了一层嵌套语句。这种编码风格非常好,尤其是当你有很长的if语句的时候(想象你需要滚动到最底层才知道还有else语句,这并不酷)

我们可以通过 倒置判断条件 & 尽早return 进一步减少if嵌套。看下面我们是怎么处理判断 条件2 的:

`
/_ 当发现无效语句时,尽早Return _/

function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

// 条件 1: 尽早抛出错误
if (!fruit) throw new Error('No fruit!');
// 条件 2: 当水果不是红色时停止继续执行
if (!redFruits.includes(fruit)) return;

console.log('red');

// 条件 3: 必须是大质量的
if (quantity > 10) {
console.log('big quantity');
}
}
`
通过倒置判断条件2,我们的代码避免了嵌套语句。这个技巧在我们需要进行很长的逻辑判断时是非常有用的,特别是我们希望能够在条件不满足时能够停止下来进行处理。

而且这么做并不困难。问问自己,这个版本(没有嵌套)是不是比之前的(两层条件嵌套)更好,可读性更高?

但对于我,我会保留先前的版本(包含两层嵌套)。这是因为:

  • 代码比较短且直接,包含if嵌套的更清晰
  • 倒置判断条件可能加重思考的负担(增加认知载荷)

因此,应当尽力减少嵌套和尽早return,但不要过度.

3.使用默认参数和解构
我猜下面的代码你可能会熟悉,在JavaScript中我们总是需要检查 null / undefined的值和指定默认值:

`
function test(fruit, quantity) {

if (!fruit) return;
// 如果 quantity 参数没有传入,设置默认值为 1
const q = quantity || 1;

console.log(We have ${q} ${fruit}!);
}

//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
实际上,我们可以通过声明 默认函数参数 来消除变量 q。
function test(fruit, quantity = 1) {
// 如果 quantity 参数没有传入,设置默认值为 1
if (!fruit) return;
console.log(We have ${quantity} ${fruit}!);
}

//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
`

这更加直观,不是吗?注意,每个声明都有自己的默认参数.

例如,我们也能给fruit分配默认值:function test(fruit = 'unknown', quantity = 1)。

如果fruit是一个object会怎么样?我们能分配一个默认参数吗?

`
function test(fruit) {
// 当值存在时打印 fruit 的值
if (fruit && fruit.name) {
console.log (fruit.name);
} else {
console.log('unknown');
}
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
`
看上面这个例子,我们想打印 fruit 对象中可能存在的 name 属性。否则我们将打印unknown。我们可以通过默认参数以及解构从而避免判断条件 fruit && fruit.name

`
// 解构 - 仅仅获取 name 属性
// 为其赋默认值为空对象
function test({name} = {}) {
console.log (name || 'unknown');
}

// test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
`
由于我们只需要 name 属性,我们可以用 {name} 解构出参数,然后我们就能使用变量 name 代替 fruit.name。

我们也需要声明空对象 {} 作为默认值。如果我们不这么做,当执行 test(undefined) 时,你将得到一个无法对 undefined 或 null 解构的的错误。因为在 undefined 中没有 name 属性。

如果你不介意使用第三方库,这有一些方式减少null的检查:

  • 使用 Lodash get函数
  • 使用Facebook开源的idx库(with Babeljs)
    这是一个使用Lodash的例子:

`
function test(fruit) {
// 获取属性名,如果属性名不可用,赋默认值为 unknown
console.log(__.get(fruit, 'name', 'unknown');
}

// test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
`
你可以在jsbin运行demo代码。除此之外,如果你是函数式编程的粉丝,你可能选择使用 Lodash fp,Lodash的函数式版本(方法变更为get或者getOr)

倾向于对象遍历而不是Switch语句

让我们看下面这个例子,我们想根据 color 打印出水果:

`
function test(color) {
// 使用条件语句来寻找对应颜色的水果
switch (color) {
case 'red':
return ['apple', 'strawberry'];
case 'yellow':
return ['banana', 'pineapple'];
case 'purple':
return ['grape', 'plum'];
default:
return [];
}
}

// test results
test(null); // []
test('yellow'); // ['banana', 'pineapple']
`
上面的代码看起来没有错误,但是我找到了一些累赘。用对象遍历实现相同的结果,语法看起来更简洁:

`
const fruitColor = {
red: ['apple', 'strawberry'],
yellow: ['banana', 'pineapple'],
purple: ['grape', 'plum']
};

function test(color) {
return fruitColor[color] || [];
}
`
或者你也可以使用 Map实现相同的结果:

`
const fruitColor = new Map()
.set('red', ['apple', 'strawberry'])
.set('yellow', ['banana', 'pineapple'])
.set('purple', ['grape', 'plum']);

function test(color) {
return fruitColor.get(color) || [];
}
`
Map是一种在 ES2015 规范之后实现的对象类型,允许你存储 key 和 value 的值。

但我们是否应当禁止switch语句的使用呢?答案是不要限制你自己。从个人来说,我会尽可能的使用对象遍历,但我并不严格遵守它,而是使用对当前的场景更有意义的方式。

** 对所有/部分 判断使用Array.every & Array.some

这最后一个建议更多是关于利用 JavaScript Array 的内置方法来减少代码行数。看下面的代码,我们想要检查是否所有水果都是红色:

`
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];

function test() {
let isAllRed = true;

// 条件:所有水果都是红色
for (let f of fruits) {
if (!isAllRed) break;
isAllRed = (f.color == 'red');
}

console.log(isAllRed); // false
}
`
代码那么长!我们可以通过 Array.every减少代码行数:

`
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];

function test() {
const isAllRed = fruits.every(f => f.color == 'red');

console.log(isAllRed); // false
}
`

现在更简洁了,不是吗?相同的方式,如果我们想测试是否存在红色的水果,我们可以使用 Array.some 一行代码实现。

`
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];

function test() {
// 条件:任何一个水果是红色
const isAnyRed = fruits.some(f => f.color == 'red');

console.log(isAnyRed); // true
}
`
``

添加了类型检测的JS深拷贝

const obj = {
        name: "benben",
        age: 22,
        hobby: {
            name: "play"
        }
    }

    const getType = (obj) => {
        return Object.prototype.toString.call(obj).slice(8, -1);
    }

    const deepCopy = (obj) => {
        let newObj;
        const type = getType(obj);
        if (type === "Object") {
            newObj = {};
        } else if (type === "Array") {
            newObj = [];
        } else {
            return obj;
        }

        for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
                if (getType(obj[key]) !== "Object" && getType(obj[key]) !== "Array") {
                    newObj[key] = obj[key]
                } else {
                    newObj[key] = deepCopy(obj[key]);
                }
            }
        }

        return newObj;
    }

    const obj2 = deepCopy(obj);
    obj2.hobby.name = "chifan";
    console.log("obj2", obj2.hobby.name); //obj2 chifan
    console.log("obj", obj.hobby.name); //obj play

基于chrome浏览器对node进行调试

本次主要介绍基于chrome浏览器对node进行调试。

安装插件

基于chrome浏览器调试需要安装node-inspector

npm install -g node-inspector

启动调试监听

 node-inspector

调试监听默认占用8080端口,修改端口可以通过

node-inspector --web-port 9999

启动node程序

 node --debug app.js

上面的方式不会在程序第一行停留,要在程序第一行停留可通过以下方式启动

 node --debug-brk=3333 gulp.js

chrome调试

在chrome浏览器中打开以下链接:

 http://127.0.0.1:8080/?port=3333

JS设计模式-策略模式

策略模式

定义:定义一系列算法,吧它们一个个封装起来,并且使他们可以相互替换

核心: 将算法的实现 和 使用分离开来实现

const errorMessage = {
    isRequired: '不能为空 ',
    minLength: '长度必须>5',
    isNumber: '必须是数字'
};

const rulesMap = {
    isRequired: function (value, errorMsg) {
        if (value === '') {
            return errorMsg || errorMessage['isRequired'];
        }
    },
    minLength: function (value, length, errorMsg) {
        if (value.length < length) {
            return errorMsg || errorMessage['minLength'];
        }
    },
    isNumber: function (value, errorMsg) {
        if (!/\d+/.test(value)) {
            return errorMsg || errorMessage['isNumber'];
        }
    }
};

function Validator() {
    this.items = [];
}
Validator.prototype = {
    constructor: Validator,
    add: function (value, rule, errorMsg) {
        const arg = [value];
        if (rule.indexOf('minLength') != -1) {
            const str = rule.split(':');
            rule = str[0];
            arg.push(str[1]);
        };
        arg.push(errorMsg);
        this.items.push(function () {
            return rulesMap[rule].apply(this, arg);
        })
    },
    start: function () {
        for (let i = 0; i < this.items.length; i++) {
            const rest = this.items[i]();
            if (rest) {
                console.log(rest)
            }
        }
    }
};


const validator = new Validator();
validator.add('', 'isRequired', '必须有值');
validator.add('2131321', 'minLength:10', '必须长度大于10');
validator.add('adada', 'isNumber', '必须是数字');
validator.start()

函数防抖实现

函数防抖定义:触发高频事件,在指定时间内函数只会执行一次,如果指定时间内高频事件再次被触发,则耗时从新开始计算

code implements

html code:

  <input id="root" type="text" />

js code:

  function debounce(fn) {
            let time = null;
            return function () {
                if (time) {clearTimeout(time); }
                time = setTimeout(() => {
                    fn.call(this);  //>= 吧input的this绑定到当前函数fn上
                }, 500);
            }
    }

  function sayOk() {
         console.log("fangdou success");
   }

  const ele = document.getElementById("root");
  ele.addEventListener("input", debounce(sayOk))

Map数据结构可以通过forEach 来动态赋值操作

    <p>1</p>
    <p>2</p>
    <p>3</p>
    <p>4</p>
    <p>5</p>
 const [p1, p2, p3, p4, p5] = document.querySelectorAll('p');
        const m1 = new Map();
        const m2 = new Map();
        const m3 = new Map();
        const m4 = new Map();
        const m5 = new Map();
        m1.set('color', 'red').set('backgroundColor', 'yellow').set('fontSize', '40px');
        m2.set('color', 'green').set('backgroundColor', 'pink').set('fontSize', '40px');
        m3.set('color', 'blue').set('backgroundColor', 'orange').set('fontSize', '40px');
        m4.set('color', '#ffaabb').set('backgroundColor', '#ccc').set('fontSize', '40px');
        m5.set('color', '#ffqqqq').set('backgroundColor', 'red').set('fontSize', '40px');
        const m = new Map()
        m.set(p1, m1).set(p2, m2).set(p3, m3).set(p4,m4).set(p5,m5);
        console.log('m', m)
        m.forEach((value,ele)=>{
            value.forEach((item,key)=>{
                ele.style[key] = item;
            })
        })

结果图
image

手动实现一个特别简单的深拷贝

递归实现一个特别简单的深拷贝
const obj = {
a: 1,
b: 2,
newObj: {
name: "hello"
}
}

const deepClone = function (resource) {
let result = {};
for (let key in resource) {
result[key] = typeof resource[key] == "object" ? deepClone(resource[key]) : resource[key]
}
return result;
}

    const obj2 = deepClone(obj);
    obj.newObj.name = "hi";
    console.log("obj", obj); // { a : 1, b : 2, newObj: { name:"hi" }}
    console.log("obj2", obj2);// { a : 1, b : 2, newObj: { name:"hello" }}

什么是BFC?

在CSS的官方文档的说明中,BFC:全称 Block formatting contexts,直译过来就是块级格式化上下文。
讲到这里你可能还是一头雾水,我们这里先停下来讲几个基本的概念以便有助于你接下来的理解。

Formatting Context 格式化上下文

它是指页面中一块独立的渲染区域,这块区域有自己独立的渲染规则,规则取决于这块区域的Box属性。
这套独立的规则规定渲染区域内的子元素如何定位以及和其他元素之间的相互关系

BFC布局规则和创建

BFC布局规则
内部的Box会在垂直方向,一个接一个地放置。

Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠

每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。

BFC的区域不会与float box重叠。

BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

计算BFC的高度时,浮动元素也参与计算

BFC的创建(满足下列条件之一)

根元素或其它包含它的元素

浮动 (元素的 float 不是 none)

绝对定位的元素 (元素具有 position 为 absolute 或 fixed)

非块级元素具有 display: inline-block,table-cell, table-caption, flex, inline-flex

块级元素具有overflow ,且值不是 visible

弹性布局 flex

那知道了怎么让元素变成BFC区域后,这个BFC到底有什么作用呢?

根据上面的描述,BFC区域有一个特点,那就是独立。不会影响外部元素。根绝这个特性就能解决很多布局问题。
1,解决外边距的塌陷问题(垂直塌陷)

<div class='div> </div>
<div class='div> </div>
 .div{
   width:50px;
    height:50px;
    background: red;
    margin: 50px;
    }

image

看上面的例子,两个盒子都有100的外边距,但是实际上两个盒子的距离却只有100px,按理来说应该是200才对,这就是margin垂直塌陷。那要解决这个问题只需要给这两个盒子都加一个父元素,并且将这个父元素设置成BFC区域,就可以解决这个margin塌陷的问题。
<div class='parent'> 
   <div class='div1> </div>
</div>
<div class='div2'> </div>
 .parent{
    overflow:hidden;
  }
 .div{
   width:50px;
   height:50px;
   background: red;
   margin: 50px;
  }

image

2,利用BFC解决包含塌陷
有时候我们给子元素加margin可能会带着父元素一起跑
image
很显然,我们只是想要子元素距离父元素50px,而不是整个父元素都一起跑。这个时候用padding可以解决问题,但是用BFC同样可以解决

只需要将父元素变为BFC区域,就能得到解决
image
3.清除浮动

<div class="parents">
  <div class="child"></div>   
</div>
.parents{
  width: 300px;
  border: 5px solid #EF6C72;
}
.child{
  width:100px;
  height: 100px;
  background-color: black;
  float: left;
}

image
可以看到,父元素里面包含了一个子元素,在子元素浮动的情况下,脱离文档流,父元素高度不会被子元素撑开,这是如果我们需要让父元素被浮动的子元素撑开,根据BFC布局规则 计算BFC的高度时,浮动元素也参与计算,我们让父级元素生成一个BFC,则父元素内的子元素也会计算高度
可以通过以下方式生成一个BFC:

.parents:{
  /*overflow: hidden;*/
  /*float: left;*/
  /*display: inline-block;*/
  /*position: absolute;*/
  }

image

react项目实现markdown,以及代码高亮处理

准备阶段:

  1. react-markdown 将markdown转化为reat version:8.0.3.
  2. react-syntax-highlighter 代码高亮,可以设置样式 version ^15.5.0
  3. remark-gfm 扩展markdown使用,让你可以在markdown中使用表格等能被编译. version:3.0.1
const docText = 
`
  ### 集成概述
    通过 Github 事件部署 devops 流水线.
    可以用过捕获 Github 事件,对计算资源或者其他下游系统进行触发部署,完成自动流水线作业。

  ### 集成用例
    通过 Github 事件完成事件通知.
    EventBridge 可快速捕获和接收 Github 变更消息,并将该变更项目对下游系统如钉钉,短信等进行通知与告警,帮助运维同学快速感知代码变更,并进行相关预案。
  
  ### 集成架构  
  ![Alt text](https://img.alicdn.com/imgextra/i1/O1CN01oTL2S529JZsRVTcHQ_!!6000000008047-2-tps-2296-1448.png)
  `;
 <ReactMarkdown
                        components={{
                            code({ node, inline, className, children, ...props }) {
                                return (
                                    <SyntaxHighlighter
                                        showLineNumbers={true} //是否展示左侧行数
                                        style={vscDarkPlus} //主题风格
                                        language={'JavaScript'} //语言类型选择, 如:css, jsx,javascript等
                                        PreTag='div'  
                                    >
                                        {String(children).replace(/\n$/, '')}
                                    </SyntaxHighlighter>
                                )
                            }
                        }}
                    >
                        {docText.replace(/\n$/, '')}
 </ReactMarkdown>

效果图:
image

不想高亮展示MD的话,可以直接使用Markdown组件

 <ReactMarkdown  children={docText} />

image

彻底搞明白 Javascript 背后的隐式类型转换

请看一段代码
[] == false //true;
!![] == true //true;
!!'' == false //true;
[1] == "1" //true;
"" == 0 //true;
"" == false //true;
[1] == true //true;
null == false //false
null == "" //false

为什么[] == false ? 是true

1.因为2个类型不同,先要进行类型转换,然后才能进行比较;
2.js语法规定,如果2个类型在进行 == 比较的时候,如果有boolean类型的值存在,先要把boolean类型的值转换为数字, false 转换为 0 ,true 转换为1
3.引用类型的对象通过toString(),转换为基本类型,然后进行比较; [].toString() // ""
4."" 转换为数字为 0
5.0 == 0 // true

为什么 [] == 0 ? 是true

1.先进行类型转换, [].toString() // ""
2."" 转换为 Number("") //0
3.0 == 0 //true

我们来尝试一些更奇葩的对象和字符串的 == 比较
'[object Object]'=={}// true

1.先进行类型转换,{}.toString() //'[object Object]'
2.'[object Object]' == '[object Object]' //true

为什么“” == 0

1.字符串和数字进行比较,会吧字符串转换为数字,然后进行比较
2."" 转换为 0
3.0 == 0 //true

为什么 null == 0 是 false呢?

1.由于null没有toString(),不能进行转换

为什么 null == undefiend 是 true?

js 底层语法实现

为什么 !![] 是 true?

1.这里面不涉及任何 == 比较, 和上面的题目完全是两类题目, 千万不可搞混
2.此题直接判断!!后面的变量 是不是不是 Falsy(假值) 即可, 只要不是Falsy这几个值, 其余情况都是 true
3.Falsy假值包括:"",0.null.undefined,false,NaN

Leecode 判定字符是否唯一

** 实现一个算法,确定一个字符串 s 的所有字符是否全都不同。 **

列子一

输入: s = "leetcode"
输出: false 

列子二

输入: s = "abc"
输出: true

map key的唯一性

var isUnique = function(astr) {
  const arr = astr.split('');  //吧一个完整的字符串按照""进行拆分成数组,每个字符对应数组中的一个元素
  const map = new Map();
  for(let i =0; i<arr.length;i++){
      map.set(arr[i],i);
  }
  const newArr = Array.from(map);
  if(newArr.length === arr.length){
    return true
  }
  return false;
};

Set 去重解决

var isUnique = function(astr){
 return new Set(astr).size === astr.length
}

设计模式-单例

单例

定义

保证一个类只有1个实例,并提供一个可以访问它的全局访问点

核型

一个类只有1个实例, 提供全局访问点

实现

假设要设置一个销售员,多次调用也仅设置一次,我们可以使用闭包缓存一个内部变量来实现这个单例

function SetManager(name){
    this.name = name;
}

SetManager.prototype.getName = function(){
    console.log('name',this.name);
}

function singleTonSetManager(fn){
    let instance = null;
    return function(){
        if(!instance){
            instance = fn.apply(this, arguments);
        }
        return instance;
    }
}

const getSetManagetSinleton = singleTonSetManager(function(name){
    const manager = new SetManager(name);
    return manager;
});


getSetManagetSinleton('abc').getName();
getSetManagetSinleton('abc').getName();

function Ruler(name){
    this.name = name;
}
Ruler.prototype.getName = function(){
    console.log('ruler is ', this.name)
}


const getSetRulesSingleton = singleTonSetManager(function(name){
    const ruler = new Ruler(name);
    return ruler;
});



getSetRulesSingleton('benben').getName();
getSetRulesSingleton('benben').getName();

JS-判断一个数字是否是素数

  • 质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数。例如:7只能被1和7整除,除此之外不能再被其他数字整除,7就是质数。
  function isPrime(n) {
            //  为2是因为质数为2,而且质数不能为1和自身不能被其他自然数整除的数叫做质数;
            for (let i = 2; i < n; i++) {
                if (n % i === 0) {
                    console.log(n, i);
                    return false //质数
                }
            }
            return true //非质数
        }
   console.log(isPrime(5)); //true
   console.log(isPrime(9)); //false

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

Array.prototype.map() 理解

 arr.map(function callback(currentValue[, index[, array]])  //> 返回一个新数组对象

这个callback一共可以接收三个参数,

  • 第一个参数代表当前被处理的元素,

  • 第二个参数代表该元素的索引。

  • 第三个参数代表原数组对象

而parseInt则是用来解析字符串的,使字符串成为指定基数的整数。

parseInt(string, radix)

接收两个参数

  • 第一个表示被处理的值(字符串)

  • 第二个表示为解析时的基数 (radix | 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。)
    了解这两个函数后,我们可以模拟一下运行情况

parseInt('1', 0) //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回1
parseInt('2', 1) //基数为1(1进制)表示的数中,1不在radix的范围内,所以无法解析,返回NaN
parseInt('3', 2) //基数为2(2进制)表示的数中,radix是2的时候, 只有2中数值(0,1),都不在3的范围内,所以无法解析,返回NaN
map函数返回的是一个数组,所以最后结果为[1, NaN, NaN]

Demo test:
['11', '11', '11',11].map(parseInt) ?
解析:

  • parseInt('11', 0), //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回11

  • parseInt('11', 1), //基数为1(1进制)表示的数中,1不在radix的范围内,所以无法解析,返回NaN

  • parseInt('11', 2), //radix为2时, (2的0次方 是1 )* 1 + (2的1次方 是2) *1这个时候返回3

  • parseInt('11', 3), //radix为3时,(3的0次方 是1 )* 1 + (3的1次方 是3) *1这个时候返回4

map函数返回的是一个数组,所以最后结果为[11, NaN, 3, 4]

最后附上MDN上对于这两个函数的链接,具体参数大家可以到里面看
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/parseInt
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map

实现JS中的apply

Function.prototype.myapply = function(context,args){
            context = context ? Object(context) : window;
            context.fn = this;
            if(!args){
                return context.fn();
            }

            const result = eval(`context.fn(${args})`);
            delete context.fn;
            return result;
        }

简单获取url中携带的参数

        const urlhref = 'http://baidu/src/index.html';
        const getParam = function (urlhref) {
            let urlParams = urlhref.split('?');
            //如果没有?后面的参数,返回‘’
            if (urlParams[0] === urlhref) {
                return ''
            }

            let paramsArr = urlParams[1].split('&');
            const obj = {};
            for (let i = 0; i < paramsArr.length; i++) {
                let param = paramsArr[i].split('=');
                obj[param[0]] = param[1];
            }
            return obj;
        }

JS设计模式-代理模式

定义

为一个对象提供一个代用品或占位符,以便控制对它的访问

核心

当客户不方便直接访问一个 对象或者不满足需要的时候,提供一个替身对象 来控制对这个对象的访问,客户实际上访问的是 替身对象。
替身对象对请求做出一些处理之后, 再把请求转交给本体对象
代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一 些额外的事情

实现

代理模式主要有三种:保护代理、虚拟代理、缓存代理
保护代理主要实现了访问主体的限制行为,以过滤字符作为简单的例子

        function sendMessage(msg) {
            console.log(msg);
        }

        function proxySendMsg(msg) {
            if (typeof msg === 'undefined') {
                console.log('deny');
                return;
            }
            msg = ('' + msg).replace(/泥\s*煤/g, '');
            sendMessage(msg);
        }

        proxySendMsg('泥煤呀泥 煤')
        proxySendMsg();

虚拟代理在控制对主体的访问时,加入了一些额外的操作
在滚动事件触发的时候,也许不需要频繁触发,我们可以引入函数防抖,这是一种虚拟代理的实现

function debounce(fn, delay) {
            delay = delay || 200;
            var time = null;
            return function () {
                const arg = arguments;
                clearTimeout(time);
                time = setTimeout(() => {
                    fn.apply(this, arg);
                }, delay);
            }
        }

        let count = 0;
        function scollHandler(e) {
            console.log(e.type, ++count);
        }

        const proxyScrollHandler = (function () {
            return debounce(scollHandler, 200);
        })()

        window.onscroll = proxyScrollHandler;

缓存代理可以为一些开销大的运算结果提供暂时的缓存,提升效率

  function add() {
            const arg = [].slice.call(arguments);
            return arg.reduce(function (a, b) {
                return a + b;
            })
        }

        const proxyAdd = (function () {
            const cache = [];
            return function () {
                const arg = [].slice.call(arguments).join(',');
                if (cache[arg]) {
                    return cache[arg];
                } else {
                    cache[arg] = arg;
                    const ret = add.apply(this, arguments);
                    return ret;
                }
            }
        })();

        console.log(
            add(1, 2, 3, 4),
            add(1, 2, 3, 4),

            proxyAdd(10, 20, 30, 40),
            proxyAdd(10, 20, 30, 40)
        );

可扩展的单例模式

单例模式

  1. 定义
    保证一个类仅有一个实例,并提供一个访问它的全局访问点
    2.核心
    确保只有1个实例,并提供全局访问

      function Person(name) {
            this.name = name;
        }

        Person.prototype.getName = function () {
            return this.name;
        }

        const singletonPerson = function (fn) {
            let instance = null;
            return function () {
                if (!instance) {
                    instance = fn.apply(this, arguments)
                }
                return instance;
            }
        };

        const getSingeTon = singletonPerson(function (name) {
            let p1 = new Person(name);
            return p1;
        })

        const s1 = getSingeTon("lol");
        const s2 = getSingeTon("cf");
        console.log("s1 == s2" , s1 == s2 ) //true

浅谈递归和--n 和 n--的区别

简单JS题,分析--n,和n--的区别:

直接上题:

  function num(n) {  
            while (n-- > 0) {
                console.log(n);
            }
        }
     num(3) 

为什么会输出2,1,0 ,而不是2,1呢?
解析:

后置递减(--): 在变量的前面,先赋值后在执行减法操作(n--);

  • 第一次进行n-->0 比较的时候, n是3,不进行--计算操作,还是保持以前的值,当执行完n-->0这个条件后,立即n 变成 2。
  • 同理,当n的值为1的入参的时候,n-->0执行完,n变为0
  function num(n) {  
            while (n-- > 0) {
                console.log(n);
            }
        }
     num(3) 

输出:2,1
解析:

前置递减(--): 在变量的前面,先自身执行减法操作后在赋值(--n);

  • 第一次进行n-->0的时候,先进行计算操作,然后吧值进行比较

一个简单循环的题

  function num(n,m){
            console.log(n);
            if(m>n){
                num(n+1,m);
                console.log(n);
            }
        }
        num(2,5);

输出:2,3,4,5,4,3,2

解析:

  • 首先,这是一个递归函数
  • 当满足 if 条件时,调用自己身,延迟执行后面的console.log(javascript 是单线程),以此类推,这可以解释为什么输出2,3,4,5
  • 当 n = 4时,调用自身,num(5, 5),外部打印5,不满足 if 判断条件,不执行 if 判断当中的内容
    此时,n = 4 的递归调用执行结束,内部的console.log 执行,打印 4,以此类推,打印4,3,2
    -这里有一个需要注意的点:为什么不是一直打印5(毕竟最终 n = 5)
    这个和函数的作用域有关,每次内部执行console.log 的时候,都是取自 if 判断处的那个 n 的值

JS- 手写深拷贝实现

核心:判断方法传入对象的值是否是对象,如果是对象,继续执行deepClone;

        function checkedType(data) {
            return Object.prototype.toString.call(data).slice(8, -1);
        }

        function deepClone(target) {
            let result = null;
            if (checkedType(target) === 'Object') {
                result = {};
            } else if (checkedType(target) === 'Array') {
                result = [];
            } else {
                return target;
            }

            for (const key in target) {
                const value = target[key];
                if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
                    result[key] = deepClone(target[key])
                } else {
                    result[key] = target[key]
                }
            }
            return result;
        };

测试1

        const obj = ['123', '456']
        const obj2 = deepClone(obj);
        console.log('obj1', obj);
        console.log('obj2', obj2);
        obj2[0] = 'wolalda';
        console.log('obj1', obj);
        console.log('obj2', obj2);

image

测试2

           const obj = {
                    name:'name1',
                    action:{
                        play1:'games1',
                        play2:'games2'
                    }
             };
            const obj2 = deepClone(obj);
            console.log('obj1', obj);
            console.log('obj2', obj2);
            obj2.name='qqd';
            console.log('obj1', obj);
            console.log('obj2', obj2);

image

浅谈事件循环机制

浏览器内核

浏览器进程: 主进程,主要负责页面管理以及管理其他进程的创建和销毁等,常驻的线程有:

  • GUI渲染线程
  • JS引擎线程
  • 事件触发线程
  • 定时器触发线程
  • HTTP请求线程
GUI渲染线程
  • 主要负责页面的渲染,解析HTML、CSS,构建DOM树,布局和绘制等。
  • 当界面需要重绘或者由于某种操作引发回流时,将执行该线程。
  • 该线程与JS引擎线程互斥,当执行JS引擎线程时,GUI渲染会被挂起,当任务队列空闲时,JS引擎才会去执行GUI渲染。
JS引擎线程
  • 该线程当然是主要负责处理 JavaScript脚本,执行代码。
  • 也是主要负责执行准备好待执行的事件,即定时器计数结束,或者异步请求成功并正确返回时,将依次进入任务队列,等待 JS引擎线程的执行
    -该线程与 GUI渲染线程互斥,当 JS引擎线程执行 JavaScript脚本时间过长,将导致页面渲染的阻塞。
事件触发线程
  • 主要负责将准备好的事件交给 JS引擎线程执行。
  • 比如 setTimeout定时器计数结束, ajax等异步请求成功并触发回调函数,或者用户触发点击事件时,该线程会将整装待发的事件依次加入到任务队列的队尾,等待 JS引擎线程的执行。
定时器触发线程
  • 主线程依次执行代码时,遇到定时器,会将定时器交给该线程处理,当计数完毕后,事件触发线程会将计数完毕后的事件加入到任务队列的尾部,等待JS引擎线程执行。
HTTP请求线程
  • 负责执行异步请求一类的函数的线程,如: Promise,anxios,ajax等。
  • 主线程依次执行代码时,遇到异步请求,会将函数交给该线程处理,当监听到状态码变更,如果有回调函数,事件触发线程会将回调函数加入到任务队列的尾部,等待JS引擎线程执行。

Render 进程 浏览器渲染进程(浏览器内核),主要负责页面的渲染、JS执行以及事件的循环。

Event Loop 是什么???

JavaScript的异步分为两种,宏任务(macro-task)和微任务(micro-task)

  • 宏任务:包括整体代码script,setTimeout,setInterval
  • 微任务:Promise.then(非new Promise),process.nextTick(node中)

执行机制:

  • 从全局任务 script开始,任务依次进入栈中,同步任务被主线程执行,执行完后出栈。
  • 遇到异步任务,交给异步处理模块处理,注册回调函数并放入事件队列里
  • 等待调用栈为空时,事件队列里的回调函数依次进入栈中执行。

当异步任务进入栈执行时,是宏任务还是微任务呢?

  • 由于执行代码入口都是全局任务 script,而全局任务属于宏任务,所以当栈为空,同步任务任务执行完毕时,会先执行微任务队列里的任务。
  • 微任务队列里的任务全部执行完毕后,会读取宏任务队列中排最前的任务。
  • 执行宏任务的过程中,遇到微任务,依次加入微任务队列。
  • 栈空后,再次读取微任务队列里的任务,依次类推。

练手DEMO

setTimeout(function() {
    console.log('setTimeout');
},1000)

new Promise(function(resolve) {
    console.log('promise');
}).then(function() {
    console.log('then');
})

console.log('console');

1.setTimeout是个宏任务,放入宏任务队列中
2.new Promise 同步代码,立即执行console.log('promise'),然后看到微任务then,因此将其放入微任务的任务队列中
3.执行同步代码console.log('console')
4.主线程的宏任务,已经执行完毕,接下来要执行微任务,因此会执行Promise.then,到此,第一轮事件循环执行完毕
5.第二轮事件循环开始,先执行宏任务,即setTimeout的回调函数,然后查找是否有微任务,没有,时间循环结束

到此做个总结,事件循环,先执行宏任务,其中同步任务立即执行,异步任务,加载到对应的的Event Queue中(setTimeout等加入宏任务的Event Queue,Promise.then加入微任务的Event Queue),所有同步宏任务执行完毕后,如果发现微任务的Event Queue中有未执行的任务,会先执行其中的任务,这样算是完成了一次事件循环。接下来查看宏任务的Event Queue中是否有未执行的任务,有的话,就开始第二轮事件循环,依此类推。

JS- 修改嵌套层级很深对象的Key

// 有一个嵌套很深层级的对象,key的形式都是a_b

        const a = {
            a_y: {
                a_z: {
                    y_x: 6
                },
                b_c: 1
            }
        };

解决方案一
1.吧对象转换成JSON字符串,然后通过正则替换掉_,
2.吧JSON字符串在解析成对象,然后返回

        function reglarExpress(obj){
            const str = JSON.stringify(obj).replace(/_/g,"");
            try{
              const result = JSON.parse(str);
              return result;
            }catch(error){
                console.log('error', error)
            }
        }

        const result =  reglarExpress(a);
        console.log('result', result);

解决方案二
递归解决,每次递归调用之前需要,判断一下传入的参数,是不是对象

        function reglarExpress(obj) {
            const keys = Object.keys(obj);
            const reuslt = {};
            keys.forEach(item => {
                const changeKey = item.replace(/_/g, '');
                if (typeof obj[item] === 'object') {
                    reuslt[changeKey] = reglarExpress(obj[item]);
                } else {
                    reuslt[changeKey] = obj[item];
                }
            })

            return reuslt;
        }

        const result = reglarExpress(a);
        console.log('result', result);

含有特殊字符的字符串转换解析处理

含有特殊字符的字符串转换解析处理

  • 如果JS字符串中含有一些特殊字符, 需要对这些特殊字符进行移除处理,
  • 只保留需要的字符,然后对这些字符进行JSON.parse()处理,得到需要的对象
    Example:
"{\n  &quot;docKey&quot;:&quot;partner.markdown.auth0&quot;,\n  &quot;stepID&quot;:&quot;428537&quot;\n}\n"

Solve:

 function stringValFilter(str) {
    let s = "";
    if (str.length === 0) {
      return "";
    }
    s = str.replace(/&amp;/g, "&");
    s = s.replace(/&lt;/g, "<");
    s = s.replace(/&gt;/g, ">");
    s = s.replace(/&nbsp;/g, " ");
    s = s.replace(/&#39;/g, "\'");
    s = s.replace(/&quot;/g, "\"");
    s = s.replace(/\n/g, ""); 
    return s;
  }

image

JS- 找出只出现一次的数字

给定一个非空整数数组,除了某个元素出现一次以为,其余每个元素均出现俩次。找出那个只出现了一次的元素
示例:
输入: [2,2,2,1]
输出: 1
**解决方案一
1.通过对象的方案

        const a = [2, 2, 1, 1, 11, 6, 11,];
        function test(a) {
            const obj = {};
            a.map(item => {
                if (obj[item]) {
                    obj[item] = ++obj[item];
                } else {
                    obj[item] = 1;
                }
            });

            let value;
            for (const key in obj) {
                if (obj[key] === 1) {
                    value = key
                }
            }
            return value;
        }

**解决方案二
1.通过2层循环,获取到二维数组,每个元素里具体的数值都是相同的。
2.然后通过fliter(), 找出长度是1的数组,然后获取里面的元素值

        function change(data) {
            const filterArr = data.map(item => {
                return data.filter((v) => {
                    return v === item;
                })
            });
            let find;
            filterArr.filter(item => {
                if (item.length === 1) {
                    find = item[0];
                }
            });
            return find;
        }

请你谈一下对于js拖拽功能的实现的理解,具体的实现方式是什么?

思路:

  • 鼠标按下时,纪录一下鼠标位置距离移动元素的边的距离。
  • 鼠标移动时,实时更新元素位置(具体计算以垂直方向为例:
  • 鼠标距离浏览器垂直方向距离,减去刚刚纪录的鼠标距离元素的垂直方向距离,
    就是元素定位top属性的值)
    鼠标松开时,完成元素位移。
   .div1 {
            width: 200px;
            height: 200px;
            background: #cccccc;
           position: relative;
        }
    const ele = document.getElementsByTagName('div')[0];
        const mouse = {
            mouseLock: false,
            mouseEleClient: {
                top: 0,
                left: 0
            }
        }

       //鼠标点击的时候
        ele.onmousedown = function (e) {
            e = e || window.event;
            var top = e.clientY;
            var left = e.clientX;
            mouse.mouseEleClient.top = top - this.offsetTop;
            mouse.mouseEleClient.left = left - this.offsetLeft;
            mouse.mouseLock = true;
        }

        //鼠标拖动的时候
        ele.onmousemove = function (e) {
            if (mouse.mouseLock) {
                var top = e.clientY;
                var left = e.clientX;
                var divTop = top - mouse.mouseEleClient.top;
                var divLeft = left - mouse.mouseEleClient.left;
                ele.style.top = divTop + 'px';
                ele.style.left = divLeft + 'px';
            }
        }

        //鼠标放开的时候
        ele.onmouseup = function (e) {
            mouse.mouseLock = false;
        }

递归实现斐波那契数列

 const cal = function(nums){
  if(nums == 0){return 0}
  if(nums == 1){return 1 }
  return cal(nums-1)+ cal(nums-2)
}
 cal(6)  //8

javascript array.sort 在不同浏览器下的兼容性问题

在开发过程中发现,各个浏览器上的Array.prototype.sort内部算法实现机制不一样,导致执行结果有偏差。
根据stackoverflow上的问答和维基百科上的结果,可以知道 chrome 目前采用快排(QuickSort)和插入排序(InsertaionSort),而对于火狐,它采用归并排序(MergeSort)。而IE使用快排。
直接看代码:

根据自己新定义的排序规则进行排序

         const arr = [
            { code: "abb", name: "abb", age: 20 },
            { code: "bbb", name: "bbb", age: 40 },
            { code: "hhb", name: "hhb", age: 18 },
            { code: "yyb", name: "yyb", age: 10 },
            { code: "bab", name: "bab", age: 16 },
            { code: "ddb", name: "ddb", age: 15 }
        ];

        arr.sort(function (a, b) {
            return a.age - b.age
        })

排序规则是根据一个number的值来进行排序的话,
各种浏览器可以保持相同的处理结果。
image

       const arr = [
            { code: "abb", name: "abb", age: 20 },
            { code: "bbb", name: "bbb", age: 40 },
            { code: "hhb", name: "hhb", age: 18 },
            { code: "yyb", name: "yyb", age: 10 },
            { code: "bab", name: "bab", age: 16 },
            { code: "ddb", name: "ddb", age: 15 }
        ];
        arr.sort(function (a, b) {
            return a.code > b.code
        })

排序规则是根据一个string的值来进行排序的话,
firefox和google等展示的结果不一样,就出现了兼容性问题

FireFox:
image

Google,IE,360等:
image

解决方案

第一种

 arr.sort(function (a, b) {
           return a.code.localeCompare(b.code) || a.code > b.code
  })

第二种:

       //判断是否Firefox浏览器
        const userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
        arr.sort(function (a, b) {
            if (userAgent.indexOf("Firefox") > -1) {
                return a.code > b.code;
            }   
            
            if (a.code < b.code) { 
              return -1 
            }else if(a.code === b.code){
              return 0
           }else{
              return 1
           }
        })

简单算法--两数之和等于目标元素的值

求数组中两数的和是目标元素的值,并且数组中的元素不能重复使用

  • 第一种解决方案,暴力双层循环实现
  function sum(numbers, target) {
            const len = numbers.length;
            const result = [];
            for (let i = 0; i < len; i++) {
                for (let j = 0; j < len - 1; j++) {
                    //数组中2个元素的值和目标元素值相同,并且数组中同一个元素不能使用2次
                    if (numbers[i] + numbers[j] === target ) {
                        result.push(i);
                        result.push(j);
                        return result;
                    }
                }
            }
        }

        const numbers = [1, 2, 3, 4, 5];
        const target = 8;
        console.log(sum(numbers, target)); //[2, 4]
  • 第二种解决方案,通过map数据结构,一层循环实现
  function sum(numbers, target) {
            const map = new Map();
            const len = numbers.length;
            for (let i = 0; i < len; i++) {
               const a = target-numbers[i];
               if(map.has(a)){
                   return [map.get(a),i]
                 }else{
                    map.set(numbers[i],i);
                 }
            }
        }

        const numbers = [1, 2, 3, 4, 5];
        const target = 8;
        console.log(sum(numbers, target));  //[2, 4]

浅谈如何处理前端异常?

为什么要处理异常?

  • 增强用户体验
  • 远程定位问题
  • 无法复线问题,尤其是移动端,机型,系统都是问题
  • 完善的前端方案,前端监控系统

对于 JS 而言,我们面对的仅仅只是异常,异常的出现不会直接导致 JS 引擎崩溃,最多只会使当前执行的任务终止。

需要处理哪些异常?

  • JS 语法错误、代码异常
  • AJAX 请求异常
  • 静态资源加载异常
  • Promise 异常
  • Iframe 异常
  • 跨域 Script error
  • 崩溃和卡顿

Try-Catch的误区:

try-catch只能捕获到同步运行时的错误,对语法和异步错误无能为力,也捕获不到

语法错误:

    try{
            let name ="aa";
            console.log(aaaaa);
        }catch(e){
            console.log("buhuo",e);
        }

异步错误

  try{
            setTimeout(() => {
                undefined.map((v)=>{
                    return v
                })
            }, 1000);
        }catch(e){
            console.log("buhuo",e);
        }

window.onerror 不是万能的

可以捕获到运行是异常

window.onerror = function(message, source, lineno, colno, error) {
console.log('捕获到异常:',{message, source, lineno, colno, error});
}
let name = 'Jartto

window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxx
onerror 最好写在所有 JS 脚本的前面,否则有可能捕获不到错误;
onerror 无法捕获语法错误;

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.