面包屑思维模型实战模型错题集结构手册流程手册自我检测专题模块

  • 微信小程序前端
    微信小程序前端易错点收集
    查看
  • css/less/sass样式控制
    在开发过程中的一些样式控制bug规避
    查看
  • tp5开发小程序
    tp5开发小程序时错误积累
    查看
  • PHP错题集
    PHP在实际开发过程中遇到的问题
    查看
  • MySql数据库
    使用MySql在实际开发中遇到的错误总结
    查看
  • TP5错题集
    积累tp5框架在实际开发过程中遇到的问题
    查看
  • uni-app爬坑
    主要用于uni-app项目中遇到的一些问题
    查看
  • Vue.js易错收集
    vue.js项目常见错误收集整理
    查看
  • uni-app开发微信小程序
    uni-app开发微信小程序的一些爬坑积累
    查看
  • Linux
    Linux在部署、开发、运维时遇见的错误积累
    查看
  • 安全设计
    常见安全设计
    查看
  • Redis
    项目中使用redis的相关错误积累
    查看
  • 前端特效
    前端特效相关错题集
    查看
more

最新博文

  • better-scroll使用时报错

    uni-app

    风口下的猪2020-03-07uni-app

    阅读更多
  • can not resolve the wrapperDom

    vue

    风口下的猪2020-03-07vue

    阅读更多
  • this.$nextTick()

    vue

    this.$nextTick()将回调延迟到下次dom更新之后执行。

    是操作含异步数据dom的必备函数

    风口下的猪2020-03-07vue

    阅读更多
  • created()和mounted()使用注意项

    vue

    created()是挂载dom树完成后开始执行,即浏览器解析数据生成dom树时触发;

    mounted()是页面初始化渲染后开始执行,即非异步数据dom的渲染完成时触发。

    两者都不适合直接触发dom操作,实际项目中的dom操作往往紧跟异步加载数据完成并渲染成功后,即在异步then()后this.$nextTick()的回调中执行dom操作。

    但一些简单的页面还是可以直接通过created()和mouted()来完成。但使用时要特别注意:

    (1)cerated中直接操作含异步数据的dom是不行的,可以使用setTimeOut()(不推荐,时间不好把控)或this.$nextTick();



    风口下的猪2020-03-07vue

    阅读更多
  • 解决[Vue warn]:Error in render:

    vue

    风口下的猪2020-03-07vue

    阅读更多
  • 前端渲染与后端渲染的区别

    web性能优化

    前端渲染:

    指的是后端返回JSON数据,前端利用预先写的html模板,循环读取JSON数据,拼接字符串(es6的模板字符串特性大大减少了拼接字符串的的成本),并插入页面。

     #  好处:网络传输数据量小。不占用服务端运算资源(解析模板),模板在前端(很有可能仅部分在前端),改结构变交互都前端自己来了,改完自己调就行。

     #  坏处:前端耗时较多,对前端工作人员水平要求相对较高。前端代码较多,因为部分以前在后台处理的交互逻辑交给了前端处理。占用少部分客户端运算资源用于解析模板。


    后端渲染:

    前端请求,后端用后台模板引擎直接生成html,前端接受到数据之后,直接插入页面。

     #  好处:前端耗时少,即减少了首屏时间,模板统一在后端。前端(相对)省事,不占用客户端运算资源(解析模板)

     #  坏处:占用服务器资源。


    前端渲染与后端渲染对比:

    (一)后端渲染:

     #  页面呈现速度:快,受限于用户的带宽

     #  流量消耗:少一点点(可以省去前端框架部分的代码)

     #  可维护性:差(前后端东西放一起,掐架多年,早就在闹分手啦)

     #  seo友好度:好

     #  编码效率:低(这个跟不同的团队不同,可能不对)

    (二)前端渲染:

     #  页面呈现速度:主要受限于带宽和客户端机器的好坏,优化的好,可以逐步动态展开内容,感觉上会更快一点。

     #  流量消耗:多一点点(一个前端框架大概50KB)当然,有的用后端渲染的项目前端部分也有在用框架。

     #  可维护性:好,前后端分离,各施其职,代码一目明了。

     #  SEO友好度:差,大量使用ajax,多数浏览器不能抓取ajax数据。

     #  编码效率:高,前后端各自只做自己擅长的东西,后端最后只输出接口,不用管页面呈现,只要前后端人员能力不错,效率不会低。


    风口下的猪2020-03-05web性能优化

    阅读更多
  • axios

    JavaScript

    一.概述

    首先,axios是基于promise用于浏览器和node.js的http客户端

    features

    • 支持浏览器和node.js

    • 支持promise

    • 能拦截请求和响应

    • 能转换请求和响应数据

    • 能取消请求

    • 自动转换json数据

    • 浏览器端支持防止CSRF(跨站请求伪造)

    二.安装

    npm安装

    $ npm install axios

    bower安装

    $ bower install axios

    通过cdn引入

    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>


    三.example

    1.发起一个get请求 

    <input id="get01Id" type="button" value="get01"/>
    <script>
        $("#get01Id").click(function () {
            axios.get('http://localhost:8080/user/findById?id=1')
                .then(function (value) {
                    console.log(value);
                })
                .catch(function (reason) {
                    console.log(reason);
                })
        })
    </script>

    另外一种形式:

    <input id="get02Id" type="button" value="get02"/>
    <script>
        $("#get02Id").click(function () {
            axios.get('http://localhost:8080/user/findById', {
                params: {
                    id: 1
                }
            })
                .then(function (value) {
                    console.log(value);
                })
                .catch(function (reason) {
                    console.log(reason);
                })
        })
    </script>

    2.发起一个post请求

    在官方文档上面是这样的:

       axios.post('/user', {
                firstName: 'Fred',
                lastName: 'Flintstone'
            }).then(function (res) {
                console.log(res);
            }).catch(function (err) {
                console.log(err);
        });

    但是如果这么写,会导致后端接收不到数据

    所以当我们使用post请求的时候,传递参数要这么写:

    <input id="post01Id" type="button" value="post01"/>
    <script>
        $("#post01Id").click(function () {
            var params = new URLSearchParams();
            params.append('username', 'sertyu');
            params.append('password', 'dfghjd');
            axios.post('http://localhost:8080/user/addUser1', params)
                .then(function (value) {
                    console.log(value);
                })
                .catch(function (reason) {
                    console.log(reason);
                });
        })
    </script>

    3.执行多个并发请求

    <input id="button01Id" type="button" value="点01"/>
    <script>
        function getUser1() {
            return axios.get('http://localhost:8080/user/findById?id=1');
        }

        function getUser2() {
            return axios.get('http://localhost:8080/user/findById?id=2');
        }

        $("#buttonId").click(function () {
            axios.all([getUser1(), getUser2()])
                .then(axios.spread(function (user1, user2) {
                    alert(user1.data.username);
                    alert(user2.data.username);
                }))
        })
    </script>

    另外一种形式:

    <input id="button02Id" type="button" value="点02"/>
    <script>
        $("#button02Id").click(function () {
            axios.all([
                axios.get('http://localhost:8080/user/findById?id=1'),
                axios.get('http://localhost:8080/user/findById?id=2')
            ])
                .then(axios.spread(function (user1, user2) {
                    alert(user1.data.username);
                    alert(user2.data.username);
                }))
        })
    </script>

    当所有的请求都完成后,会收到一个数组,包含着响应对象,其中的顺序和请求发送的顺序相同,可以使用axios.spread分割成多个单独的响应对象


     


    三、 axiosAPI

    (一)可以通过向axios传递相关配置来创建请求

    axios(config)

    <input id="buttonId" type="button" value="点"/>
    <script>
        $("#buttonId").click(function () {
            var params = new URLSearchParams();
            params.append('username','trewwe');
            params.append('password','wertyu');
            // 发起一个post请求
            axios({
                method:'post',
                url:'http://localhost:8080/user/addUser1',
                data:params
            });
        })
    </script>

    axios(url[,config])

    <input id="buttonId" type="button" value="点"/>
    <script>
        $("#buttonId").click(function () {
            // 发起一个get请求,(get是默认的请求方法)
            axios('http://localhost:8080/user/getWord');
        })
    </script>

    (二)请求方法别名

    axios.request(config)

    axios.get(url[, config])

    axios.delete(url[, config])

    axios.head(url[, config])

    axios.options(url[, config])

    axios.post(url[, data[, config]])

    axios.put(url[, data[, config]])

    axios.patch(url[, data[, config]])

    // 在使用别名方法时,url、method、data这些属性都不必在配置中指定

    (三)并发请求,即是帮助处理并发请求的辅助函数

    //iterable是一个可以迭代的参数如数组等
    axios.all(iterable)
    //callback要等到所有请求都完成才会执行
    axios.spread(callback)

    (四)创建实例,使用自定义配置

    1.axios.create([config])

    var instance = axios.create({
      baseURL:"http://localhost:8080/user/getWord",
      timeout:1000,
      headers: {'X-Custom-Header':'foobar'}
    });

    2.实例的方法

    以下是实例方法,注意已经定义的配置将和利用create创建的实例的配置合并

    axios#request(config)
    axios#get(url[,config])
    axios#delete(url[,config])
    axios#head(url[,config])
    axios#post(url[,data[,config]])
    axios#put(url[,data[,config]])
    axios#patch(url[,data[,config]])

     


    四、请求配置

    请求的配置选项,只有url选项是必须的,如果method选项未定义,那么它默认是以get方式发出请求

    {
        // url 是用于请求的服务器 URL
        url: '/user/getWord',
        // method 是创建请求时使用的方法
        method: 'get', // 默认是 get
        // baseURL 将自动加在 url 前面,除非 url 是一个绝对路径。
        // 它可以通过设置一个 baseURL 便于为 axios 实例的方法传递相对路径
        baseURL: 'http://localhost:8080/',
        //  transformRequest 允许在向服务器发送前,修改请求数据
        // 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
        // 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
        transformRequest: [function (data) {
            // 对 data 进行任意转换处理
            return data;
        }],
        //  transformResponse 在传递给 then/catch 前,允许修改响应数据
        transformResponse: [function (data) {
            // 对 data 进行任意转换处理
            return data;
        }],
        //  headers 是即将被发送的自定义请求头
        headers: {'X-Requested-With': 'XMLHttpRequest'},
        //  params 是即将与请求一起发送的 URL 参数
        // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
        params: {
            ID: 12345
        },
        //  paramsSerializer 是一个负责 params 序列化的函数
        // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
        paramsSerializer: function (params) {
            return Qs.stringify(params, {arrayFormat: 'brackets'})
        },
        //  data 是作为请求主体被发送的数据
        // 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
        // 在没有设置 transformRequest 时,必须是以下类型之一:
        // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
        // - 浏览器专属:FormData, File, Blob
        // - Node 专属: Stream
        data: {
            firstName: 'yuyao'
        },
        //  timeout 指定请求超时的毫秒数(0 表示无超时时间)
        // 如果请求话费了超过 timeout 的时间,请求将被中断
        timeout: 1000,
        //  withCredentials 表示跨域请求时是否需要使用凭证
        withCredentials: false, // 默认的
        //  adapter 允许自定义处理请求,以使测试更轻松
        // 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
        adapter: function (config) {
            /* ... */
        },
        //  auth 表示应该使用 HTTP 基础验证,并提供凭据
        // 这将设置一个 Authorization 头,覆写掉现有的任意使用 headers 设置的自定义 Authorization 头
        auth: {
            username: 'zhangsan',
            password: '12345'
        },
        //  responseType 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
        responseType: 'json', // 默认的
        // xsrfCookieName 是用作 xsrf token 的值的cookie的名称
        xsrfCookieName: 'XSRF-TOKEN', // default
        //  xsrfHeaderName 是承载 xsrf token 的值的 HTTP 头的名称
        xsrfHeaderName: 'X-XSRF-TOKEN', // 默认的
        //  onUploadProgress 允许为上传处理进度事件
        onUploadProgress: function (progressEvent) {
            // 对原生进度事件的处理
        },
        //  onDownloadProgress 允许为下载处理进度事件
        onDownloadProgress: function (progressEvent) {
            // 对原生进度事件的处理
        },
        //  maxContentLength 定义允许的响应内容的最大尺寸
        maxContentLength: 2000,
        //  validateStatus 定义对于给定的HTTP 响应状态码是 resolve 或 reject  promise 。
        // 如果 validateStatus 返回 true (或者设置为 null 或 undefined ),promise 将被 resolve; 否则,promise 将被 rejecte
        validateStatus: function (status) {
            return status >= 200 && status < 300; // 默认的
        },
        //  maxRedirects 定义在 node.js 中 follow 的最大重定向数目
        // 如果设置为0,将不会 follow 任何重定向
        maxRedirects: 5, // 默认的
        //  httpAgent 和 httpsAgent 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
        //  keepAlive 默认没有启用
        httpAgent: new http.Agent({keepAlive: true}),
        httpsAgent: new https.Agent({keepAlive: true}),
        // 'proxy' 定义代理服务器的主机名称和端口
        //  auth 表示 HTTP 基础验证应当用于连接代理,并提供凭据
        // 这将会设置一个 Proxy-Authorization 头,覆写掉已有的通过使用 header 设置的自定义 Proxy-Authorization 头。
        proxy: {
            host: '127.0.0.1',
            port: 9000,
            auth: {
                username: 'lisi',
                password: '54321'
            }
        },
        //  cancelToken 指定用于取消请求的 cancel token
        cancelToken: new CancelToken(function (cancel) {
        })
    }

     


    五、响应内容

    {
      data:{},
      status:200,
      //从服务器返回的http状态文本
      statusText:'OK',
      //响应头信息
      headers: {},
      //config是在请求的时候的一些配置信息
      config: {}
    }

    可以这样来获取响应信息

    <input id="getId" type="button" value="get"/>
    <script>
        $("#getId").click(function () {
            axios.get('http://localhost:8080/user/findById?id=1')
                .then(function (value) {
                    console.log("data:"+value.data);
                    console.log("status:"+value.status);
                    console.log("statusText:"+value.statusText);
                    console.log("headers:"+value.headers);
                    console.log("config:"+value.config);
                })
                .catch(function (reason) {
                    console.log(reason);
                })
        })
    </script>

     


    六、默认配置

    默认配置对所有请求都有效

    1、全局默认配置

    axios.defaults.baseURL = 'http://localhost:8080/';
    axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
    axios.defaults.headers.post['content-Type'] = 'appliction/x-www-form-urlencoded';

    2、自定义的实例默认设置

    //当创建实例的时候配置默认配置
    var instance = axios.create({
        baseURL: 'http://localhost:8080/'
    });
    //当实例创建时候修改配置
    instance.defaults.headers.common["Authorization"] = AUTH_TOKEN;

    3、配置中有优先级

    config配置将会以优先级别来合并,顺序是lib/defauts.js中的默认配置,然后是实例中的默认配置,最后是请求中的config参数的配置,越往后等级越高,后面的会覆盖前面的例子。

    //创建一个实例的时候会使用libray目录中的默认配置
    //在这里timeout配置的值为0,来自于libray的默认值
    var instance = axios.create();
    //回覆盖掉library的默认值
    //现在所有的请求都要等2.5S之后才会发出
    instance.defaults.timeout = 2500;
    //这里的timeout回覆盖之前的2.5S变成5s
    instance.get('/longRequest',{
      timeout: 5000
    });

     


    七、拦截器

    1.可以在请求、响应在到达then/catch之前拦截

    //添加一个请求拦截器

    axios.interceptors.request.use(function(config){
      //在请求发出之前进行一些操作
      return config;
    },function(err){
      //Do something with request error
      return Promise.reject(error);
    });

    //添加一个响应拦截器

    axios.interceptors.response.use(function(res){
      //在这里对返回的数据进行处理
      return res;
    },function(err){
      //Do something with response error
      return Promise.reject(error);
    })

    2.取消拦截器

    var myInterceptor = axios.interceptor.request.use(function(){/*....*/});
    axios.interceptors.request.eject(myInterceptor);

    3.给自定义的axios实例添加拦截器

    var instance = axios.create();
    instance.interceptors.request.use(function(){})

     


    八、错误处理

    <input id="getId" type="button" value="get"/>
    <script>
        $("#getId").click(function () {
            axios.get('http://localhost:8080/user/findById?id=100')
                .catch(function (error) {
                    if (error.response) {
                        //请求已经发出,但是服务器响应返回的状态吗不在2xx的范围内
                        console.log("data:"+error.response.data);
                        console.log("status:"+error.response.status);
                        console.log("header:"+error.response.header);
                    } else {
                        //一些错误是在设置请求的时候触发
                        console.log('Error', error.message);
                    }
                    console.log(error.config);
                });
        })
    </script>

     


    九、取消

    通过一个cancel token来取消一个请求

    通过CancelToken.source工厂函数来创建一个cancel token

    <input id="getId" type="button" value="get"/>
    <input id="unGetId" type="button" value="unGet"/>
    <script>
        var CancelToken = axios.CancelToken;
        var source = CancelToken.source();
        $("#getId").click(function () {
            axios.get('http://localhost:8080/user/findById?id=1', {
                cancelToken: source.token
            })
                .then(function (value) {
                    console.log(value);
                })
                .catch(function (thrown) {
                if (axios.isCancel(thrown)) {
                    console.log('Request canceled', thrown.message);
                } else {
                    //handle error
                }
            });
        });
        $("#unGetId").click(function () {
            //取消请求(信息的参数可以设置的)
            source.cancel("操作被用户取消");
        });
    </script>

     



    风口下的猪2020-03-05JavaScript

    阅读更多
  • Generator生成器

    JavaScript

    1.相关概念

    1)为什么要引入Generator?
    众所周知,传统的JavaScript异步的实现是通过回调函数来实现的,但是这种方式有两个明显的缺陷:

    • 缺乏可信任性。例如我们发起ajax请求的时候是把回调函数交给第三方进行处理,期待它能执行我们的回调函数,实现正确的功能
    • 缺乏顺序性。众多回调函数嵌套使用,执行的顺序不符合我们大脑常规的思维逻辑,回调逻辑嵌套比较深的话调试代码时可能会难以定位。

    Promise恢复了异步回调的可信任性,具体参见(欸欸这个往后放),而Generator正是以一种看似顺序、同步的方式实现了异步控制流程,增强了代码可读性。

    2)概念:

    • Generator(生成器)是一类特殊的函数,跟普通函数声明时的区别是加了一个*号,以下两种方式都可以得到一个生成器函数:
    // 声明方式一(个人比较偏向这种风格啦)
    function *main() {
        // do something……
    }
    
    // 声明方式二
    function* main() {
        // do something
    }
    
    • Iterator(迭代器):当我们实例化一个生成器函数之后,这个实例就是一个迭代器。可以通过next()方法去启动生成器以及控制生成器的是否往下执行。
    • yield/next:这是控制代码执行顺序的一对好基友。
      通过yield语句可以在生成器函数内部暂停代码的执行使其挂起,此时生成器函数仍然是运行并且是活跃的,其内部资源都会保留下来,只不过是处在暂停状态。
      在迭代器上调用next()方法可以使代码从暂停的位置开始继续往下执行。
    // 首先声明一个生成器函数
    function *main() {
        console.log('starting *main()');
        yiled; // 打住,不许往下走了
        console.log('continue yield 1');
        yield; // 打住,又不许往下走了
        console.log('continue yield 2');
    }
    // 构造处一个迭代器it
    let it = main(); 
    
    // 调用next()启动*main生成器,表示从当前位置开始运行,停在下一个yield处
    it.next(); // 输出 starting *main()
    
    // 继续往下走
    it.next(); // 输出 continue yield 1
    
    // 再继续往下走
    it.next(); // 输出 continue yield 2
    
    

    以上是一个非常简单的yield/next相互配合控制代码执行的例子,认真看的同学可能会产生一个疑问:
    next()居然比yield多了一个???
    没错,就是这样的,因为let it = main(); 进行实例化之后,main()里的代码不会主动执行。第一个next()永远是用于启动生成器,生成器启动后要想运行到最后,其内部的每个yield都会对应一个next(),所以说next()永远都会比yield多一个了~~



    2.消息传递

    生成器的作用之一是消息传递。通过yield ...和next(...)组合使用,可以在生成器的执行过程中构成一个双向消息传递系统。

    • 当next(..)执行到yield语句处时会暂停生成器的执行,同时next(...)会得到一个带有value属性的对象,yield语句后面带的值会赋给value(如果yield后面没有值,value就为undefined)。可以将yield ...效果看成跟return ...类似。

    • 当生成器处于暂停状态时,暂停的yield表达式处可以接收下一个启动它的next(...)传进来的值。当next(...)使生成器继续往下执行时,其传入的值会将原来的yield语句替换掉。

    看个栗子:

    function *main() {
        let x = yield "starting";
        let y = yield (x * 2);
    
        console.log(x, y);
        return x + y;
    }
    
    let it = main();
    
    let res = it.next(); // 第一个next()用于启动生成器
    console.log(res.value);  // 输出"starting" (yield语句后跟的值传给了next()的对象)
    
    res = it.next(5); // 向等待的第一个yield传入值5,*main()中的 x 被赋值为5
    console.log(res.value); // 输出10 (x * 2得到了10传给next(5)运行后的对象)
    
    res = it.next(20); // 向等待的第二个yield传入值20, *main()中的x被赋值为20
                       // 输出5 20    (执行后面的console.log(x, y)语句分别输出x,y的值)
    console.log(res.value); // 输出25  (return ...的值传给了next(20)运行后的对象)
    

    注意:

    1. 第一个next()仅仅是用于启动生成器用的,并不会传入任何东西,如果传入了参数也会被自动忽略掉。
    2. yield ...在值传递方面的作用相当于return ...,你也可以把它当做一个return语句来看待,如果yield后面不加参数,则默认yield undefined;
    3. 最后一个next()执行完毕之后,得到的值是*main()函数return出来的值,如果函数没有自己加return语句,一样也会默认return undefined;
    4. next()执行完毕后会返回一个对象,属性值有两个,分别为value(从yield或return处拿到的值)和done(boolean值,标识生成器是否执行完毕)。

    接下来尝试一下异常传值的情况:

    function *main() {
        let x = yield "starting";
        let y = yield (x * 2);
        console.log(x, y);
    }
    
    let it = main();
    
    let res = it.next('1111'); // '1111'被丢弃啦~~
    console.log(res.value);  // 输出"starting"
    
    res = it.next(); // 不给yield传值 x成了undefined
    console.log(res.value); // 输出NaN (undefined * 2得到了NaN传给next()运行后的对象)
    
    res = it.next(); // 不给yield传值 y未拿到值
                     // 输出undefined undefined
    console.log(res.value); // 输出undefined  (默认return undefined;)


    3.Generator在流程控制中的应用##

    基础概念说完了,那么,Generator是如何解决传统回调中存在的缺乏顺序性问题的呢?首先来看下面一个使用传统回调函数实现异步的例子:

    function getCallSettings() {
        // utils.ajax方法用于发起ajax请求
        utils.ajax({
            url: '/dialer/dialerSetting',
            method: "GET",
            success: (res) => {
                let settingInfo = res.dialerSetting;
                dealData(settingInfo);
            },
            error: (err) => {
                console.log(err);
            }
        });
    }
    function dealData(data) {
        // do something……
    }
    getCallSettings();
    

    可以看出,dealData只能在ajax请求拿到数据之后才能运行,所以需要嵌套在success回调中执行。以上例子嵌套的不深,依赖settingInfo的地方也不多,只有一个dealData函数,所以看起来还好,但是,试想一下如果接下来的很多其他请求都依赖于该请求返回的数据,或者很多代码逻辑都需要拿到settingInfo之后才能进行,那么代码可读性就会差很多了。
    所以,接下来尝试以生成器的方式实现以上场景:

    function getCallSettings() {
        utils.ajax({
            url: '/dialer/dialerSetting',
            method: "GET",
            success: (res) => {
                it.next(res.dialerSetting); // 将res.dialerSetting传给yield表达式
            },
            error: (err) => {
                it.throw(err); // 抛出错误
            }
        });
    }
    function *dealData() {
        try{
           let settingInfo = yield getCallSettings();
           // do something……
        }
        catch(err) {
          console.log(err); // 接收错误
        }
    }
    let it = dealData();
    it.next(); // 启动生成器
    

    此处的yield是用于在异步流程中暂停阻塞代码,当然,它阻塞的只有生成器里面的代码,生成器外部的丝毫不受影响。let settingInfo = yield getCallSettings();中,通过yield把异步的流程完全抽离出去,实现了看似顺序同步的代码,这无疑是巨大的改进。



    4.Generator+Promise实现完美异步##

    1.如果将Generator和Promise结合在一起使用,既让代码看起来顺序同步,又恢复了可信任性,可以说是非常完美的了。
    接下来就把以上例子改成Generator + Promise实现:

    function getCallSettings() {
        // utils.ajax方法支持返回promise对象,把得到的promise return出去
        return utils.ajax({
            url: '/dialer/dialerSetting',
            method: "GET",
        });
    }
    function *dealData() {
        try {
            let settingInfo = yield getCallSettings();
            // do something……
        }
        catch(err) {
            console.log(err); // 接收错误
        }
    }
    
    let it = dealData();
    let promise = it.next().value; // 注意,这里拿到yield出来的promise
    promise.then(
        (info) => {
            it.next(info); // 拿到info传给yield表达式
        }, 
        (err) => {
            it.throw(err); // 抛出错误
        }
    );
    

    2.这种方式的另一个好处在于,当多个Promise并发请求时,正确的写法可以更好地提高性能。
    例如以下场景:

    // 满屏都是代码,这里代码尽量精简些啦
    function *dealData() {
        let r1 = yield utils.ajax(reqUrl1); // 请求1获取到 r1
        let r2 = yield utils.ajax(reqUrl2); // 请求2获取到 r2
    
        let reqUrl3 = getUrl(reqUrl1, reqUrl2); // 请求3需要的url依赖于前面两个请求
        let r3 = yield utils.ajax(reqUrl3);
        // do something……
    }
    

    以上写法中,生成器执行时会先发出请求1,请求1返回后才会发出请求2,请求2返回之后,再发出请求3。其实在这里请求1和2之间不存在依赖关系,是可以同时进行的。所以还可以用一种效率更高的写法:

    // 满屏都是代码,这里代码也尽量精简些啦
    function *dealData() {
        let p1 = utils.ajax(reqUrl1); // 请求1获取到 r1
        let p2 = utils.ajax(reqUrl2); // 请求2获取到 r2
    
        let r1 = yield p1;
        let r2 = yield p2;
    
        let reqUrl3 = getUrl(reqUrl1, reqUrl2); // 请求3需要的url依赖于前面两个请求
        let r3 = yield utils.ajax(reqUrl3);
        // do something……
    }
    

    这样p1和p2之间就可以同时进行,不会相互阻塞啦~~是不是很棒棒呢


    5.async和await##

    以上yield + Promise的写法需要我们对拿到的promise的决议进行人工处理(区分成功或失败),在ES7中提供了async/await帮我们省掉了这个步骤:async/await组合的出现使得异步的世界更加完美啦~~
    下面改写一下代码实现形式:

    function getCallSettings() {
        return utils.ajax({
            url: '/dialer/dialerSetting',
            method: "GET",
        });
    }
    async function dealData() {
        try {
            let settingInfo = await getCallSettings(); // await会暂停在这,直到promise决议(请求返回)
            // do something……
        }
        catch(err) {
            console.log(err);
        }
    }
    dealData();
    

    dealData函数不需要再被声明为生成器函数,而是声明为async函数;
    同时,其内部也不用yield出一个promise,而是用await进行等待,直到promise决议。

    那么async/await的写法和yield相比孰优孰劣呢?
    其实个人感觉两者都有自己独到的长处,没有优劣之分(纯属个人见解,不喜勿喷)

    • async/await在处理promise的层面上省略了对决议的人工处理,让代码量得以减少,语义上也更容易理解。
    • yield包容性更广泛,async只能接口promise,yield除此之外还能接收字符串、数组、对象等各种类型的数据。


    6.yield 委托##

    为什么要用委托呢?因为,一个代码组织合理的程序中,出于功能模块化等原因,我们很可能在一个生成器中调用另外一个生成器,比如在a()中调用b(),但是通常情况下a()的实例中是无法使用next方法对b()内部进行操控的,所以这个时候我们就可以使用yield将b()委托给a()。

    function *a() {
        console.log('a start');
        yield 2;
        console.log('a end');
    }
    function *b() {
        console.log('b start');
        yield 1;
        yield *a(); // 将a委托给b
        yield 3;
        console.log('b end');
    }
    
    let it = b();
    
    it.next().value;    // b start
                        // 1 
    it.next().value;    // a start
                        // 2
    it.next().value;    // a end
                        // 3
    it.next().value;    // b end
    

    合理地进行生成器分离和使用委托,可以使代码可读性更强,更易维护~~~~~


    风口下的猪2020-03-05JavaScript

    阅读更多
  • Iterator遍历器(迭代器)

    JavaScript

    一.概述

    遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

    Iterator 的作用有三个:

    (1)为各种数据结构,提供一个统一的、简便的访问接口;

    (2)使得数据结构的成员能够按某种次序排列;

    (3)ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

    Iterator 的遍历过程是这样的。

    (1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

    (2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

    (3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

    (4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

    每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

    var it = makeIterator(['a', 'b']);
    
    it.next() // { value: "a", done: false }
    it.next() // { value: "b", done: false }
    it.next() // { value: undefined, done: true }
    
    function makeIterator(array) {
      var nextIndex = 0;
      return {
        next: function() {
          return nextIndex < array.length ?
            {value: array[nextIndex++], done: false} :
            {value: undefined, done: true};
        }
      };
    }


    二.默认的Iterator接口

    let demo=数据结构[Symbol.iterator]();

    Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环(详见下文)。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。

    一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。

    ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名Symbol.iterator,它是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内(参见《Symbol》一章)。

    const obj = {
      [Symbol.iterator] : function () {
        return {
          next: function () {
            return {
              value: 1,
              done: true
            };
          }
        };
      }
    };
    

    上面代码中,对象obj是可遍历的(iterable),因为具有Symbol.iterator属性。执行这个属性,会返回一个遍历器对象。该对象的根本特征就是具有next方法。每次调用next方法,都会返回一个代表当前成员的信息对象,具有valuedone两个属性。

    ES6 的有些数据结构原生具备 Iterator 接口(比如数组),即不用任何处理,就可以被for...of循环遍历。原因在于,这些数据结构原生部署了Symbol.iterator属性(详见下文),另外一些数据结构没有(比如对象)。凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。

    原生具备 Iterator 接口的数据结构如下。

    • Array
    • Map
    • Set
    • String
    • TypedArray
    • 函数的 arguments 对象
    • NodeList 对象

    下面的例子是数组的Symbol.iterator属性。

    let arr = ['a', 'b', 'c'];
    let iter = arr[Symbol.iterator]();
    
    iter.next() // { value: 'a', done: false }
    iter.next() // { value: 'b', done: false }
    iter.next() // { value: 'c', done: false }
    iter.next() // { value: undefined, done: true }
    

    上面代码中,变量arr是一个数组,原生就具有遍历器接口,部署在arrSymbol.iterator属性上面。所以,调用这个属性,就得到遍历器对象。


    阮一峰ES6手册 : https://es6.ruanyifeng.com/#docs/iterator

    风口下的猪2020-03-05JavaScript

    阅读更多
  • Promise(一)之概述

    JavaScript

    一.作用和意义

    Promise是ES6中的一个内置的对象(实际上是一个构造函数,通过这个构造函数我们可以创建一个Promise对象),它是为了解决异步问题的。Promise意在让异步代码变得干净直观。

    promise有三种状态:

    (1)Pending--进行中

    (2)Fulfilled(或者Resolved)--已完成

    (3)Rejected--已失败

    promise只有两种状态变化:

    (1)从Pending(进行中)到Resolved(完成)

    (2)从Pending(进行中)到Rejected(已失败)

    Promise也是有一定的缺点的,如在Pengding时,我们无法取消状态,另外,我们没法判断Pending究竟是刚刚开始的Pending还是即将要完成的Pending。


    二.使用

    Promise是一个本地对象,要使用它,就需要new

    1.使用promise对象

    (1)普通对象使用Promise

    let test=new Promise((resolve,rejected)=>{
           sync...//同步代码
           if(respons.status===200){
                   resolve(response); //控制状态从pending往resolved变化
           }else{
                   rejected(err);  //控制状态从pending往rejected变化
           }
    })
    test.then(
          (res)=>{
                 console.log(res);
          },
          (error)=>{
                 console.log(error)
          }
    )

    (2)函数使用promise

    此时函数使用promise,要将函数看作函数对象,即函数内部要return这个promise

    function test(){
         return new Promise((resolve,rejected)=>{
                 sync...//同步代码
           if(respons.status===200){
                   resolve(response); //控制状态从pending往resolved变化
           }else{
                   rejected(err);  //控制状态从pending往rejected变化
           }
         })
    }
    test().then(
         (res)=>{
                  console.log(res);
         },
         (error)=>{
                  console.log(error);
         }
    );

    2.promise状态手动控制

    promise实例化时,参数是一个函数,而该函数的参数是两个函数,即resolve和rejected

    (1)在该函数中执行resolve(),promise的状态由pending变为resolved,会触发then()内第一个方法;

    (2)在该函数中执行rejected(),promise的状态由pending变为rejected,会触发then()内第二个方法或catch()

    3.结果传递

    promise异步操作的结果

    (1)从手动的resolve(res)    传到then((res)=>{})

    (2)从手动的rejected(error)    传到then(()=>,(error)=>{})

    (3)从Error本地对象    传到catch((error)=>{})



    三.then()方法

     #  .then()是定义在promise原型对象即Promise.prototype上的方法,

     #  它是 Promise 实例状态改变时的回调函数

     #  then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

     #  then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

    getJSON("/post/1.json").then(function(post) {
      return getJSON(post.commentURL);
    }).then(function (comments) {
      console.log("resolved: ", comments);
    }, function (err){
      console.log("rejected: ", err);
    });


    四.catch()方法

     #  Promise.prototype.catch方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

    p.then((val) => console.log('fulfilled:', val))
      .catch((err) => console.log('rejected', err));
    
    // 等同于
    p.then((val) => console.log('fulfilled:', val))
      .then(null, (err) => console.log("rejected:", err));

    上面代码中,promise抛出一个错误,就被catch方法指定的回调函数捕获。注意,上面的写法与下面两种写法是等价的。

    // 写法一
    const promise = new Promise(function(resolve, reject) {
      try {
        throw new Error('test');
      } catch(e) {
        reject(e);
      }
    });
    promise.catch(function(error) {
      console.log(error);
    });
    
    // 写法二
    const promise = new Promise(function(resolve, reject) {
      reject(new Error('test'));
    });
    promise.catch(function(error) {
      console.log(error);
    });

    比较上面两种写法,可以发现reject方法的作用,等同于抛出错误。

    如果 Promise 状态已经变成resolved,再抛出错误是无效的。

     #  一般总是建议,Promise 对象后面要跟catch方法,这样可以处理 Promise 内部发生的错误。catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。

    const someAsyncThing = function() {
      return new Promise(function(resolve, reject) {
        // 下面一行会报错,因为x没有声明
        resolve(x + 2);
      });
    };
    
    someAsyncThing()
    .catch(function(error) {
      console.log('oh no', error);
    })
    .then(function() {
      console.log('carry on');
    });
    // oh no [ReferenceError: x is not defined]
    // carry on

    上面代码运行完catch方法指定的回调函数,会接着运行后面那个then方法指定的回调函数。如果没有报错,则会跳过catch方法。



    风口下的猪2020-03-05JavaScript

    阅读更多
  • 本地对象、内置对象、宿主对象

    JavaScript

    废话不多说,先上图



    这个图来自于《JavaScript语言精髓与编程实践》第三章P184页。最近在改第二版,这张图重做了,需要的可以对照着看。

      此外,补充一下图中用到的概念:

      1、内置(Build-in)对象与原生(Naitve)对象的区别在于:前者总是在引擎初始化阶段就被创建好的对象,是后者的一个子集;而后者包括了一些在运行过程中动态创建的对象。

      2、引擎扩展对象是一个并不太大的集合,一般来说比较确定,它们也属于引擎的原生对象(但不属于ECMA规范的原生对象)。

      3、宿主对象不是引擎的原生对象,而是由宿主框架通过某种机制注册到JavaScript引擎中的对象。

      4、一些宿主会把自己提供的对象/构造器也称为“原生对象”,例如Internet Explorer 7就把它提供的XMLHttpRequest()称为原生的——与此相对的是在它的更早先版本中通过“new ActiveXObject('Microsoft.XMLHTTP')”这样的方法创建的对象。这种情况下,读者应注意到“宿主的原生对象”与“引擎的原生对象”之间的差异。


    JavaScript 本地对象、内置对象、宿主对象

    本地对象(要NEW)

    ECMA-262 把本地对象(native object)定义为“独立于宿主环境的 ECMAScript 实现提供的对象”。

    Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError、ActiveXObject(服务器方面)、Enumerator(集合遍历类)、RegExp(正则表达式)

    由此可以看出,简单来说,本地对象就是 ECMA-262 定义的类(引用类型)。


    内置对象(不要NEW  直接引用——只有MATH  GLOBAL)

    ECMA-262 把内置对象(built-in object)定义为“由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现”。这意味着开发者不必明确实例化内置对象,它已被实例化了。

    同样是“独立于宿主环境”。根据定义我们似乎很难分清“内置对象”与“本地对象”的区别。而ECMA-262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。

    如此就可以理解了。内置对象是本地对象的一种。而其包含的两种对象中,Math对象我们经常用到,可这个Global对象是啥东西呢?

    Global对象是ECMAScript中最特别的对象,因为实际上它根本不存在!在ECMAScript中,不存在独立的函数,所有函数都必须是某个对象的方法。

    类似于isNaN()、parseInt()和parseFloat()方法等,看起来都是函数,而实际上,它们都是Global对象的方法。而且Global对象的方法还不止这些。有关Global对象的具体方法和属性,感兴趣的同学可以看一下这里:JavaScript 全局对象参考手册

    宿主对象(BOM  DOM  &  自定义对象)

    何为“宿主对象”?  ECMAScript中的“宿主”当然就是我们网页的运行环境,即“操作系统”和“浏览器”。所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象。

    所有的BOM和DOM对象都是宿主对象。因为其对于不同的“宿主”环境所展示的内容不同。其实说白了就是,ECMAScript官方未定义的对象都属于宿主对象,因为其未定义的对象大多数是自己通过ECMAScript程序创建的对象。TML DOM 是 W3C 标准(是 HTML 文档对象模型的英文缩写,Document Object Model for HTML)。

    HTML DOM 定义了用于 HTML 的一系列标准的对象,以及访问和处理 HTML 文档的标准方法。

    通过 DOM,可以访问所有的 HTML 元素,连同它们所包含的文本和属性。可以对其中的内容进行修改和删除,同时也可以创建新的元素。

    DHTML DOM 独立于平台和编程语言。它可被任何编程语言诸如 Java、JavaScript 和 VBScript 使用。



    风口下的猪2020-03-05JavaScript

    阅读更多
  • 软件开发
  • 素质要求
  • 计算机基础
  • 架构
  • 安全
  • 性能
  • 运维
  • 尾页
  • 数据库
  • 开发终端
  • 语言基础
  • 项目管理
  • 产品设计
  • 系统
  • 工作规范
  • 计算机网络
  • 前端技术栈
  • 数据结构
  • 计算机组成原理
  • 后端技术栈
  • 性能优化
  • 安全设计
  • 常见模块
  • 计算机操作系统
  • 服务器
  • python
  • MySQL
  • thinkphp
  • PHP
  • Java
  • JavaScript
  • Windows
  • Linux
  • 特效
  • indexedDB
  • vue
  • 淘宝联盟
  • Ionic
  • Angular
  • 微信小程序
  • 支付宝小程序
  • uni-app
  • css/sass/less
  • 支付
  • socket
  • 爬虫
  • web性能优化
  • 消息推送
  • CVM
  • sqlite
  • Redis
  • 前端基础
  • 基础
  • element
  • Nginx
  • yii2
  • /ponder/index/catelist/catelist/cateid/10.html

    我的名片

    网名:风口下的猪

    职业:软件开发、广告传媒

    现居:重庆渝北

    Email:kamoneyte@qq.com

    标签云

    站点信息

    • 文章统计:528篇
    • 移动端访问:扫码进入SQ3R