Code Monkey home page Code Monkey logo

blog's Introduction

blog's People

Contributors

sanjings avatar

Stargazers

li feng avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

blog's Issues

浏览器相关基础知识总结

浏览器相关基础知识总结

1. 常用浏览器有哪些?内核分别是什么?

  • chrome浏览器:以前使用WebKit内核,现在使用Blink内核;
  • firefox浏览器:Gecko内核;
  • safari浏览器:WebKit内核;
  • opera浏览器:以前使用Presto内核,被360收购后,短暂使用过webkit,现在使用Blink内核;
  • IE浏览器:Trident内核
  • Edge浏览器:之前使用EdgeHTML内核(Trident的升级版),现在使用的chromium内核

2. 浏览器渲染页面的流程?

  1. 构建DOM树,也就是解析DOM节点的过程,采用深度优先原则;
  2. 构建CSS树,也就是解析CSS的过程,把当前浏览器不兼容的CSS属性屏蔽掉;
  3. DOM树和CSS树构建完成后,合成render树,浏览器根据render树渲染页面。render树每个节点都被当成一个盒子,都有自己的样式,render树不包含display:none和head标签里面的节点。

【注】:现代浏览器都是边解析边构建渲染树边渲染页面,不会等到DOM树和CSS树都构建完成后才生成render树。

3. 什么是回流和重绘?

当js对页面进行节点操作时,就会产生回流或者重绘,回流时,浏览器会重新构建受影响部分的render树,就会发生重绘。
回流: 当节点的尺寸,位置,display:none变成display:block时,render树的一部分或者全部需要重新构建,这就叫回流;
重绘: 回流完成后,浏览器根据新的render树重新渲染受影响的部分节点,这个过程叫重绘;

引起回流的原因:

  • dom节点的增加,删除;
  • dom节点的位置变化;
  • dom节点的宽高、边距、填充、边框、文字大小变化时;
  • dom节点的display显示是否;
  • 页面初始化渲染时;
  • 浏览器窗口尺寸变化时;
  • 向浏览器请求某些样式信息时:offset、scroll、client、width、height、getComputedStyle(),currentStyle()
  • 只引起重绘,不引起回流的方式:只影响元素的外观,不影响元素的布局的,就只会重绘,不会回流。
  • 节点的颜色,背景颜色改变时;
  • visibility显示与否

4. 浏览器加载HTML文档的时间线?

概念:从浏览器加载页面开始到页面加载完成的过程中,按顺序发生的每一个时间的总流程叫时间线。

  1. js引擎生成document对象,浏览器开始解析文档,构建DOM树,document.readyState="loading";
  2. 解析到link标签,就异步加载css文件,加载完成后解析css,构建css树;解析到style标签,同样也是异步构建css树;
  3. 解析到script标签,加载外部js脚本时,如果没有设置异步加载(defer或async),则浏览器暂停解析,由js引擎加载js脚本并执行,执行完成后浏览器继续解析;如果设置了异步加载,则不会阻塞dom树的构建;
  4. 解析到img和viedo标签,异步加载图片和视频资源,不阻塞浏览器解析文档;
  5. 文档解析完成,dom树构建完成,document.readyState=“interactive”,同时设置了defer的script标签所加载的js脚本按照顺序执行,并且触发DOMContentloaded事件;
  6. 所有js脚本加载并执行完成,img和viedo加载的图片资源和视频资源也加载完成后,window.onlaod事件触发,并且document.readyState="complete"。表示页面加载完成。

document.onreadystatechange事件会监听文档解析加载的变化,也就是readyState的值的变化。

5. 浏览器的怪异模式和标准模式是什么?

标准模式: 浏览器按照W3C标准解析并执行代码;
怪异模式: 按照浏览器自己的方式解析并执行代码,因为不同浏览器解析的方式不一样,所以叫做怪异模式。
浏览器解析时使用标准模式还是怪异模式,与文档中的DTD声明有关,DTD声明定义了标准文档的类型,声明了DTD,则浏览器会根据声明的文档类型解析文档,没有声明DTD,则变成怪异模式。

使用document.compatMode可以查看当前的解析模式:

  • CSS1Compat:标准模式;
  • BackCompat:怪异模式;

标准模式和怪异模式的区别:

  1. 盒模型不一样;
  2. 标准模式无法操作行内元素的宽高,怪异模式可以;
  3. 标准模式下,子元素设置百分比高度是根据父元素高度定的,父元素没有设置固定高度,则子元素设置百分比高度无效,怪异模式下,父元素没有固定高度,子元素也可以设置百分比高度,根据窗口的高度而定;
  4. 怪异模式下,IE的margin:0 auto无效

封装图片懒加载&预加载

封装图片懒加载&预加载

懒加载

当一个页面需要展示大量的图片列表时,可以使用懒加载技术,减少网络请求,提升页面渲染速度

懒加载函数

/**
 * 懒加载函数
 * @param {domObject}  oImages img标签的dom集合  
 * @param {domObject} containnerEl 滚动的容器,默认为document.documentElement
 * @return {Function} 返回懒加载函数
 */
const imgLazyload = (oImages, containerEl = document.documentElement) => {
         const LEN = oImages.length; 
         let index = 0; // 计算已经加载了的图片数量

         // 如果容器不是document,则给容器加上相对定位,用于获取图片到容器顶部的距离
         if (containerEl !== document.documentElement) {
            containerEl.style.position = 'relative';
         }

         return () => {
            const viewHeight = containerEl.clientHeight,  // 容器的视口高度
                      scrollTop = containerEl.scrollTop; // 滚动条距离容器顶部的高度

            for (let i = index; i < LEN; i++) {
               const imgItem = oImages[i]

               // 如果图片距离容器顶部的高度小于容器的视口高度与滚动条滚动的距离, 就把图片的地址赋值给src属性,同时index+1
               if (imgItem.offsetTop < viewHeight + scrollTop) {
                  imgItem.src = imgItem.dataset.src;
                  index++;
               }
            }
         }  
}

使用(伪代码):

<style>
      .container{
         display: flex;
         justify-content: space-between;
         flex-wrap: wrap;
         margin: 0 auto;
         width: 1000px;
         height: 500px; 
         overflow: auto; 
      }
      .container img{
         margin-top: 50px;
         width: 400px;
         height: 300px;
      }
</style>

<div class="container">
       // default.png 用于占位的图片 图片的url放在data-src属性中
      <img src="default.png" data-src="https://xxx.oss.com/images/12313131.jpg" />
      <img src="default.png" data-src="https://xxx.oss.com/images/12313132.jpg" />
      <img src="default.png" data-src="https://xxx.oss.com/images/12313133.jpg" />
</div>

const oContainer = doacument.querySelector('.container'),
          oImages = oImgWrapper.getElementsByTagName('img');

imgLazyload(oImages, oContainer )() // 首次加载显示图片
oContainer .addEventListener('scroll', throttle(imgLazyload(oImages, oContainer)), false) // 对scroll事件做节流处理

预加载

当图片太大,加载太慢,可以使用预加载技术,等图片请求回来后,再渲染页面

预加载函数

/**
 * 预加载函数
 * @param {domObject}  oImages img标签的dom集合  
 * @param {Array}  imgUrls 图片urls  
 * @return {Void}
 */
const preloadImg = (container, imgUrls) => {
      
      imgUrls.forEach(item => {
         const image = new Image()
         image.src = item
         
         image.onload = () => {
            container.appendChild(image)
         }
      })
}

使用(伪代码):

<style>
      .container{
         display: flex;
         justify-content: space-between;
         flex-wrap: wrap;
         margin: 0 auto;
         width: 1000px;
      }
      .container img{
         margin-top: 50px;
         width: 400px;
         height: 300px;
      }
</style>

<div class="container"></div>

const oContainer = doacument.querySelector('.container');
const imgUrls = [
      'https://xxx.oss.com/images/12313131.jpg',
      'https://xxx.oss.com/images/12313132.jpg',
      'https://xxx.oss.com/images/12313133.jpg'
]

preloadImg(oContainer , imgUrls)

函数防抖&节流

函数防抖&节流

函数节流与函数防抖都是为了限制函数的执行频次,是一种性能优化的方案。

防抖函数

定义:被防抖函数包裹的函数在一段时间后才会执行,如果在这段时间内再次调用,则重新计算执行时间;当预定时间内没有再次调用该函数,则真正执行该函数。

应用场景:

  • 按钮提交事件(防止连续点击导致的重复提交);
  • window.onresize事件(窗口变化完成后再执行相应逻辑);
  • input输入事件(等待用户连续输入完成后再执行相应逻辑);
  • 监听顶部下拉刷新事件;
 /**
  * 防抖函数
  * @params {Function} 要执行的函数
  * @params {Number} 延迟执行毫秒数
  * @return {Function}
  */
function debounce (fn, delayTime) {
  let timer = null;
  return function () {
    var _this = this;

    timer && clearTimeout(timer);
    timer = setTimeout(function(){
      fn.apply(_this, arguments);
    }, delayTime)
  }
}

节流函数

定义:被节流函数包裹的函数,在规定时间内多次调用,也只会执行一次。

应用场景:

  • window.onscroll事件;
  • mousemove事件;
  • input输入事件(在用户连续输入过程中请求服务器数据来模糊搜索,使用节流减少不必要的频繁请求);
 /**
  * 节流函数
  * @params {Function} fn 要执行的函数
  * @params {Number} waitTime 间隔执行的时间
  * @return {Function}
  */
function throttle (fn, waitTime) {
  var preTime = 0;
  return function() {
    var nowTime = new Date().getTime();

    if (nowTime - preTime >= waitTime) {
      fn.apply(this, arguments);
      preTime = nowTime;
    }
  }
}

防抖节流合并版

防抖有时候触发的太频繁会导致一次响应都没有,所以结合节流在间隔时间结束后必须给用户一个响应

 /**
  * 防抖节流合并版
  * @params {Function} fn 要执行的函数
  * @params {Number} waitTime 间隔执行的时间
  * @return {Function}
  */
function throttle (fn, waitTime) {
  var timer = null, preTime = 0;
  return function() {
    var _this = this;
    var nowTime = new Date().getTime();

    timer && clearTimeout(timer);
    if (nowTime - preTime < waitTime) {
      timer = setTimeout(function() {
        preTime = nowTime;
        fn.apply(_this, arguments);
      }, waitTime);
    } else {
      fn.apply(_this, arguments);
      preTime = nowTime;
    }
  }
}

手动实现一个Promise

手动实现一个Promise

Promise简单说明

  • Promise主要用于解决异步回调的问题
  • 存在三个状态(state)pending、fulfilled、rejected
  • pending(等待态)为初始态,并可以转化为fulfilled(成功态)和rejected(失败态),状态一旦变化,就不会再更改

实现思路

  • 使用ES6 class声明一个类
  • 接受一个fn函数作为参数,并且这个函数会在实例化时被执行
  • 定义value为成功时的值,reason为失败时的值
  • fn函数接收两个函数参数,一个用于改变成功态(resolve),一个用于改变失败态(reject)
  • 当resolve或reject在异步函数中执行时,由于then优先于resolve或reject执行,这时state还是pending, 所以将他们的在then里面的回调处理函数存放起来,一旦异步结束后调用resolve或者reject时,就执行他们各自的回调处理函数

以下是实现代码

class MyPromise {
  constructor(fn) {
    if (!fn || typeof(fn) !== 'function') {
      throw new Error('需要传入函数作为参数')
    }

    this.state = 'pending' // 初始化状态
    this.value = null // resolve成功的值
    this.reason = null // reject失败的值
    this.resolveCallbacks = [] // 存放成功的回调数组
    this.rejectCallbacks = [] // 存放失败的回调数组

    const resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value;
        this.resolveCallbacks.forEach(cb => cb(value))
      }
    }

    const reject = reason => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = reason
        this.rejectCallbacks.forEach(cb => cb(reason))
      }
    }

    try{
      fn(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  then(onResolve, onReject) {
    onResolve = typeof(onResolve) === 'function' ? onResolve : value => value;
    onReject = typeof(onReject) === 'function' ? onReject : reason => reason;
    
    switch(this.state) {
      // resolve或者reject在异步函数中执行时,state还是pendding状态,将他们的回调处理函数先保存,一旦resolve或者reject时,就调用
      case 'pending':
        this.resolveCallbacks.push(onResolve); // 将成功的回调存到resolveCallbacks里面
        this.rejectCallbacks.push(onReject) // 将失败的回调存到rejectCallbacks里面
        break;
      case 'fulfilled': 
        onResolve(this.value)
        break;
      case 'rejected':
        onReject(this.reason)
        break;
    }
  }
}

加深理解,手写数组常用方法!

加深理解,手写数组常用方法

push()

添加内容至数组末尾,参数可0个或多个,返回修改后的长度,会改变原数组

Array.prototype.myPush = function () {
  for (var i = 0; i < arguments.length; i++) {
    this[this.length] = arguments[i]
  }

  return this.length
}

pop()

移除数组最后一项,返回移除的那一项,会改变原数组

Array.prototype.myPop = function () {
  if (!this.length) return undefined;

  var popItem = this[this.length - 1]
  this.length -= 1

  return popItem
}

shift()

移除数组第一项,返回移除的那一项,会改变原数组

Array.prototype.myShift = function () {
  if (!this.length) return undefined;

  var shiftItem = this[0]
  for (var i = 0; i < this.length - 1; i++) {
    this[i] = this[i + 1]
  }
  this.length -= 1

  return shiftItem
}

unshift()

添加内容至数组开头,参数可0个或多个,返回修改后的长度,会改变原数组

Array.prototype.myUnshift = function () {
  for (var i = arguments.length + this.length - 1; i >= 0; i--) {
    if (i > arguments.length - 1) {
      this[i] = this[i - arguments.length]
    } else {
      this[i] = arguments[i]
    }
  }

  return this.length
}

reverse()

反转数组的顺序,返回修改后的数组,会改变原数组

Array.prototype.myReverse = function () {
  var lastIndex = this.length - 1

  for (var i = 0; i < this.length; i++) {
    var temp = this[i]
    this[i] = this[lastIndex]
    this[lastIndex] = temp
    if (i === lastIndex || lastIndex - i === 1) {
      break;
    }
    lastIndex--
  }

  return this
}

concat()

将内容添加到数组的末尾,也能将多个数组连接起来,返回新的数组,不改变原数组

Array.prototype.myConcat = function () {
  var arr = JSON.parse(JSON.stringify(this)),
      toStr = Object.prototype.toString;

  for (var i = 0; i < arguments.length; i++) {
    if (toStr.call(arguments[i]) === '[object Array]') {
      for (var j = 0; j < arguments[i].length; j++) {
        arr[arr.length] = arguments[i][j]
      }
    } else {
      arr[arr.length] = arguments[i]
    }
  }

  return arr
}

forEach()

对数组进行遍历,没有返回值

Array.prototype.myForEach = function (callback) {
  if (!callback) throw new Error('请传入回调函数')

  var context = arguments[1] || window,
      len = this.length;

  for (var i = 0 ; i < len; i++) {
    callback.apply(context, [this[i], i, this])
  }
}

map()

数组“映射”,对数组进行遍历,返回每次函数调用的结果组成的新数组

Array.prototype.myMap = function (callback) {
  if (!callback) throw new Error('请传入回调函数')

  var context = arguments[1] || window,
      len = this.length,
      newArr = [];

  for (var i = 0 ; i < len; i++) {
    newArr.push(callback.apply(context, [this[i], i, this]))
  }

  return newArr
}

filter()

数组“过滤”,对数组进行遍历,返回满足过滤条件的项组成的新数组

Array.prototype.myFilter = function (callback) {
  if (!callback) throw new Error('请传入回调函数')

  var context = arguments[1] || window,
      len = this.length,
      newArr = [];

  for (var i = 0; i < len; i++) {
    if (Boolean(callback.apply(context, [this[i], i, this]))) {
      newArr.push(this[i])
    }
  }

  return newArr
}

find()

数组“搜索”,对数组进行遍历,返回第一个满足搜索条件的项,如果都不满足,返回undefined。

Array.prototype.myFind = function (callback) {
  if (!callback) throw new Error('请传入回调函数')

  var context = arguments[1] || window,
      len = this.length;

  for (var i = 0; i < len; i++) {
    if (Boolean(callback.apply(context, [this[i], i, this]))) {
      return this[i]
    }
  }
}

every()

遍历数组,判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回true,否则返回false

Array.prototype.myEvery = function (callback) {
  if (!callback) throw new Error('请传入回调函数')

  var context = arguments[1] || window,
      len = this.length;

  for (var i = 0; i < len; i++) {
    if (!Boolean(callback.apply(context, [this[i], i, this]))) {
      return false
    }
  }

  return true
}

some()

遍历数组,判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true,都不满足返回false

Array.prototype.mySome = function (callback) {
  if (!callback) throw new Error('请传入回调函数')

  var context = arguments[1] || window,
      len = this.length;

  for (var i = 0; i < len; i++) {
    if (Boolean(callback.apply(context, [this[i], i, this]))) {
      return true
    }
  }

  return false
}

reduce()

遍历数组,累加计算数组的每一项,返回计算总和

Array.prototype.myReduce = function (callback, initialValue) {
  if (!callback) throw new Error('请传入回调函数')

  var total = 0,
      i = 0,
      len = this.length;

  if (typeof initialValue === 'undefined') {
    total = this[0]
    i = 1
  } else {
    total = initialValue
  }

  for (i; i < len; i++) {
    total = callback(total, this[i], i, this)
  }

  return total
}

JavaScript中Object的常用apis

JavaScript中Object的常用apis

在JavaScript中,几乎所有的对象都是Object的实例,它们的原型链的顶端是Object.prototype,这篇文章总结一下Object的常用静态方法。

1. Object.create():使用指定的原型对象和属性创建一个新对象

const origin = {
  name: 'sanjing',
  sayName() {
     console.log(this.name)
  }
}
const target = Object.create(origin)
target.sayName() //  sanjing

相当于target.prototype = origin
当然,也可以传入null,表示没有原型的对象

const target = Object.create(null)
console.log(target.__proto__) // undefined

与new Object()的区别:Object.create()可以自定义原型,可以利用这一点来实现类继承,而new Object()是默认原型(Object.prototype)

2.Object.getPrototypeOf():返回指定对象的原型,如果没有原型返回null

const obj = {
  name: 'sanjing'
}
console.log(Object.getPrototypeOf(obj)) // 因为obj的原型是Object.prototype,所以返回Object.prototype

const obj2 = Object.create(null)
console.log(Object.getPrototypeOf(obj2)) // null

3.Object.freeze():冻结对象,返回被冻结的对象(常用)

被冻结后的对象不能修改原型,不能添加新属性,不能删除已有属性,不能修改它的可枚举性、可配置型、可写性,也不能修改已有属性的值。

const obj = {
  name: 'sanjing'
}
const freezeObj = Object.freeze(obj)
obj.name = 'norton'
freezeObj.sex = 'male'
delete obj.name
console.log(obj.name) // sanjing
console.log(freezeObj) // { name: 'sanjing' }

因为const 声明的对象,只能保证引用地址不被修改,里面的属性可以随意修改,所以可以使用该方法,彻底冻结对象

4.Object.seal():封闭对象,返回被封闭的对象

被封闭后的对象不能添加新属性,不能删除已有属性,不能修改它的可枚举性、可配置型、可写性。

const obj = {
  name: 'sanjing'
}
const sealObj = Object.seal(obj)
obj.name = 'norton'
console.log(obj.name) // 'norton' 
obj.age = 27
console.log(obj) { name: 'norton' }
delete obj.name
console.log(obj) { name: 'norton' }

和Object.freeze()的区别:Object.freeze()冻结的对象中的现有属性值是不可变的。用Object.seal()密封的对象可以改变其现有属性值。

5.Object.assign(target, ...origins):合并对象,将两个或多个对象合并,返回合并后的对象(常用)

第一个参数就是合并后的对象,之后的参数是待合并的对象,一个或多个

const origin1 = {
  name: 'sanjing'
}
const origin2 = {
  age: 27
}

const target = Object.assign({}, origin1, origin2) 
console.log(target) // {name: 'sanjing', age: 27}

Object.assign(origin1, origin2)
console.log(origin1) // {name: 'sanjing', age: 27}

【注意】:Object.assign()是按照顺序合并,如果有相同属性,则覆盖

const origin1 = {
  name: 'sanjing'
}
const origin2 = {
  name: 'norton'
}

const target = Object.assign({}, origin1, origin2) 
console.log(target) // {name: 'norton'}

6.Object.is():判断两个值是否相等(常用)

【注意】:和===的区别是,===将+0和-0看作相等,并且NaN和NaN不等

const a = +0
const b = -0
console.log(a===b) // true
console.log(Object.is(a, b)) // false

const c = NaN
console.log(c === NaN) // false
console.log(Object.is(c, NaN)) // true

7.Object.keys():返回一个指定对象的自身可枚举属性组成的数组(常用)

const obj = {
  name: 'sanjing',
  age: 27,
  career: 'Web Development Engineer '
}
const keys = Object.keys(obj)
console.log(keys) // ['name', 'age', 'career']

8.Object.values():返回一个指定对象的自身可枚举属性值组成的数组(常用)

const obj = {
  name: 'sanjing',
  age: 27,
  career: 'Web Development Engineer '
}
const values= Object.keys(obj)
console.log(values) // ['sanjing', 27, 'Web Development Engineer']

9.Object.entries():返回一个指定对象的自身可枚举属性的键值对组成的数组

const obj = {
  name: 'sanjing',
  age: 27,
  career: 'Web Development Engineer '
}
const entries= Object.entries(obj)
console.log(entries) 
[
  [ 'name', 'sanjing' ],
  [ 'age', 27 ],
  [ 'career', 'Web Development Engineer ' ]
]

【注意】:Object.keys(),Object.values(),Object.entries()这三个方法,都不会遍历原型上的属性,for in则会遍历出原型上可枚举的属性

10.Object.defineProperty():定义对象的属性,新增或修改现有属性,返回该对象

const obj = {
  name: 'sanjing',
  age: 27
}
Object.defineProperty(obj, 'name', {
  value: 'norton',
  writable:  false, // 可写性 false的时候表示该属性不能重新赋值
  configurable: false, // 可配置性 false的时候表示该属性不可配置,也就是不可删除
  // enumerable: false, // 可枚举性 false的时候表示该属性不可枚举
})

obj.name = 'norton'
console.log(obj.name) sanjing
delete obj.name
console.log(obj.name) sanjing

当然Object.defineProperty()最有名的使用场景是Vue的响应式数据绑定,利用对象属性的getter函数和setter函数来做数据劫持,然后改变视图,实现数据改变=>视图相应改变,具体的Vue的实现原理不是本文要总结的范围,感兴趣的推荐看黄轶老师的Vue.js源码全方位深入解析

const obj = {
   name: 'sanjing'
}
let name = obj.name
Object.defineProperty(obj, 'name', {
   get() {
     return name
   },
   set(newValue) {
     name = 'name:' + newValue
    console.log('name is changed')
   }
})
obj.name = 'norton'
console.log(obj.name) // 'name is changed'   ’name:norton‘

如上通过配置对象属性的getter和setter,可以实现在访问或修改属性值时,做出相应的处理
【注意】:get、set不能和value、writable同时设置,使用Object.defineProperty()新增一个属性时,writable、configurable、enumerable默认都是false, value默认undifined

11.Object.defineProperties():功能和Object.defineProperty()一样,可以同时定义多个属性

const obj = {}
Object.defineProperties(obj, {
   'name': {
      value: 'sanjing',
      writable: true,
      enumerable: true
   },
   'sex': {
      value: 'male',
      writable: false,
      enumerable: true
   }
});
console.log(obj) // { name: 'sanjing', sex: 'male' }

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.