lea_

lea_ 查看完整档案

广州编辑广州华立科技职业技术学院  |  服装设计 编辑宇宙中心  |  前端 编辑 github.com/z-ao 编辑
编辑

在有限时间,做最好自己.

个人动态

lea_ 发布了文章 · 2019-06-28

前端设计——数据转换层

前言

在工作中,经常会遇到,接口的数据格式与页面布局/交互不匹配的情况,需要前端进行处理。 心想:“数据处理与业务无关,单独抽离一个模块来写吧。” 于是,转换层就此诞生。

第一版设计

当我设计模块时,
第一步 会明确模块的职责。
转换层——顾名思义,把接口数据格式 转换 成页面所需要格式。

第二步 制定与其他模块对接规则。
因为它是从页面模块抽离出来的,所以只有页面模块才能引用它。
而且逻辑单一(把输入数据处理后输出),所以它只能引用工具模块。

第三步 划分子模块。
模块主要是处理数据的问题,所以根据数据类型的维度划分子模块。

第一版设计大功告成
// 消息通知信息的转换方法
// transform/notice.js
export default{
    show(data) {
        ....
        return ret;
    }
}
// 面板页使用
// page/dashboard
import noticeModel from 'model/notice.js'; //发送消息通知请求类
import noticeTransform  from 'transform/notice.js';
//转换成页面所需要接口格式
const data = await noticeModel.show().then(res => noticeTransform.show(res));

缺陷!!!
第一版设计中,我们很难看出某个转换方法,被那一个或几个页面使用。
随着项目复杂度不断增大,以后接手的小伙伴根本就不敢改/删转换层里的代码。

第二版设计

在第一版设计中,遇到转换方法与使用页面对应不明确的问题。
思考后,决定调整划分子模块方式,改为根据页面维度划分

第二版成品
// 面板页里的消息通知信息转换方法
// transform/dashboard.js
export default{
    noticeShow(data) {
        ....
        return ret;
    }
}
// 面板页使用
// page/dashboard
import noticeModel from 'model/notice.js'; 
import dashboardTransform  from 'transform/dashboard.js';

const data = await noticeModel.show().then(res => dashboardTransform.noticeShow(res));

缺陷 Again!!!
虽然能清晰识别页面具有那些转换方法,
但是,如果A与B、C...页面,需要对相同的数据转成相同格式。
这样的模块划分,对相似代码抽离是不友好的。

第三版设计

在第二版设计中,遇到相似的代码难以复用的问题。
在第三版设计,也是从调整划分子模块方式下手,改回数据类型的维度划分
同时,规范方法命名。
给页面模块使用方法名= model名 + 'for' + 页面名称,
私有方法名= '_' + model名 + 格式语义。

第三版成品
// A、B页面 要把消息通知信息转换一句提示
// transform/notice.js
const transform = {
    _showOneTip(data) {
        ....
        return ret;
    },
    showForA(data){
        return this._showOneTip(data);
    },
    showForB(data){
        return this._showOneTip(data);
    }
}

export default transform;

总结

业务会不断迭代优化,其实代码也需要不断迭代优化的过程。
在过程中不断思考,尽可能把项目设计的更具有结构性。
当然,每次更新项目底层设计,工作量是不少,所以也要多感谢团队支持。

难点

  • 同一个数据,有可能不同页面有不同格式。
  • 如何把模块之间的联系更加明确。
  • 如何复用具有相同逻辑代码。

细节

  • 转换方法不能修改传入数据。

江湖救急
笔者有两年多前端开发经验(地点北京),比较擅长vue与性能优化。
希望能进入具有Geek精神团队,一起折腾,一起做有意思事情。
图片描述

查看原文

赞 1 收藏 0 评论 0

lea_ 发布了文章 · 2018-08-02

初探 event loop

事件循环

了解知识点

  • 线程
  • 执行栈
  • task queue
  • web api
  • macro task
  • micro task

线程

javascript是单线程的语言,
可以单线程将理解为只有一条车道,在车道里后面的车在等前面的车通过后,才能通过.
即当前面的程序没有执行,后面的程序也不能执行.

执行栈

执行栈像"车道",被执行的程序会放入执行栈里,
但它的执行的顺序是,后面进来的程序先执行.

示例

视频源地址

task queue

在线程中有很多等待运行的任务(程序,而执行栈只会放入一个任务.
其他可运行任务会放入任务队列中.
这里虽说是个队列, 它的执行的顺序,不会先进的程序先执行.
每个event loop都会有一个或多个任务队列

web api

javascript是单线程,但也能实现异步,这种实现基与页面提供很多API,如(setTimeout, ajax, addEventListener ...)
这些都是异步函数,也就是说,运行到异步函数时,
把异步函数里闭包放入web api里,等待正确的时机,
web api会把闭包放入task queue里执行.

示例

图片描述

macro task

macro task有setTimeout ,setInterval, setImmediate,requestAnimationFrame,I/O ,UI渲染...
task queue 是由很多个macro task组成的队列,

micro task

micro task有Promise, process.nextTick, Object.observe, MutationObserver...
每个event loop都会有一个micro task

event loop

执行流程


  1. 当执行栈为null时
  2. 看task queue的第一个macro task是不是null,如果不是取出放入执行栈,如果是跳转5
  3. 执行栈运行task
  4. 运行完毕,把task设置null,并移出
  5. 执行 micro task队列
    a. 看micro task队列第一task个是不是null,如果不是取出放入执行栈,如果是跳转f
    b. 执行栈运行task
    c. 运行完毕,把task设置null,并移出
    d. 看micro task队列下一个task是不是null,如果不是跳转b
    f. 结束micro task队列执行
  6. 跳转到1

示例

图片描述

实战

<script>// 创建macro task, 因为stack 为null, 执行
(function test() {
  setTimeout(() => { // 创建macro task, 当前task queue['第一setTimeout闭包']
    console.log(4)
  }, 0);
  new Promise(resolve => {
    console.log(1); // 执行, 控制台 1
    for(var i = 0; i < 10000; i++) {
      i == 9999 && resolve(); // 创建micro task, 当前micro task queue['第一then闭包']
    }
    console.log(2); // 执行, 控制台 1 2
  }).then(() => {
    console.log(5);
    return new Promise(resolve => {
        resolve()
    })
  }).then(() => {
      console.log(6)
  });
  console.log(3); /* 执行, 控制台 1 2 3, 第一个script标签创建的macro task结束
                     运行micro task queue首个micro task === 第一then闭包
                       执行, 控制台 1 2 3 5, 
                     并创建了micro task 放入 micro task queue['第二then 闭包']
                     运行micro task queue首个micro task === 第二then 闭包
                     执行, 控制台 1 2 3 5 6
                  */
})()
</script>

<script>// 创建macro task, 因为stack 为null, 执行
(function test2() {
  setTimeout(function () { // 创建一个macro task 放入 task queue['第一setTimeout闭包' '第二setTimeout闭包']
    console.log(42)
  }, 0);
  new Promise(function executor (resolve) {
    console.log(12); // 执行 控制台1 2 3 5 6 12
    for(var i = 0; i < 10000; i++) {
      i == 9999 && resolve(); // 创建micro task, 当前micro task queue['第一then闭包']
    }
    console.log(22); // 执行 控制台1 2 3 5 6 12 22
  }).then(function() {
    console.log(52);
  });
  console.log(32); /* 执行, 控制台 1 2 3 5 6 12 22 32, 第二个script标签创建的macro task结束
                     运行micro task queue首个micro task === 第一then闭包
                       执行, 控制台 1 2 3 5 6 12 22 32 52, 
                     查看micro task queue首个task为null, 查看task queue
                     task queue为空,向web 获取可执行的任务['第一setTimeout闭包', '第二setTimeout闭包']
                     运行task queue首个 macro task === 第一setTimeout闭包
                     执行, 控制台 1 2 3 5 6 12 22 32 52 4   
                     查看micro task queue首个task为null, 查看task queue
                     运行task queue首个 macro task === 第二setTimeout闭包
                     执行, 控制台 1 2 3 5 6 12 22 32 52 4 42
                  */
})()
</script>

猜想

macro/micro task 进入执行栈时,中间应该会有一个缓存区,
例如

<script> // 创建macro task,进入执行栈
var a = () => {
    console.log('a');
}

var b = () => {
    console.log('b'); 
}

a(); //这时函数A进入stack,执行,打印出a
b(); //这时函数B进入stack,执行,打印出b
</script>

如果是把整个macro task放入执行栈,
按后进程序先执行的机制, 应该会先打印'b',
但打印的是'a',说明函数b是等函数a执行完后再进入执行栈的,
所以在macro task 会把里面的函数拆分为一个执行的队列,放入执行栈里.

示例

图片描述

参考资料

https://github.com/ccforward/cc/issues/48
https://juejin.im/entry/596d78ee6fb9a06bb752475c

Make a friend

作者有一年半的前端开发经验,比较擅长性能优化和vue,喜欢对各种原理的深究,
喜欢篮球和电影
如果有趣味相投可以加入微信群
图片描述

查看原文

赞 1 收藏 1 评论 0

lea_ 提出了问题 · 2018-07-30

js instanceof判断问题

问题描述

类型判断

问题出现的环境背景及自己尝试过哪些方法

类型判断

相关代码

a = true
b = new Boolean(true)
b instanceof Boolean // true
a.constructor === b.constructor // true

why?
a instanceof Boolean ? false

你期待的结果是什么?实际看到的错误信息又是什么?

是否能分析a = true和 new Boolean(true)区别, instanceof判断原理

关注 5 回答 3

lea_ 提出了问题 · 2018-07-30

js instanceof判断问题

问题描述

类型判断

问题出现的环境背景及自己尝试过哪些方法

类型判断

相关代码

a = true
b = new Boolean(true)
b instanceof Boolean // true
a.constructor === b.constructor // true

why?
a instanceof Boolean ? false

你期待的结果是什么?实际看到的错误信息又是什么?

是否能分析a = true和 new Boolean(true)区别, instanceof判断原理

关注 5 回答 3

lea_ 评论了文章 · 2018-07-30

Javascript事件循环机制以及渲染引擎何时渲染UI

JavaScript的一大特点就是单线程,而这个线程中拥有唯一的一个事件循环。

事件循环基本概念

  • JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。
  • 一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。
  • 任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。
  • macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
  • micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)
  • setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的具体执行任务。
// setTimeout中的回调函数才是进入任务队列的任务
setTimeout(function() {
    console.log('xxxx');
})
// 非常多的同学对于setTimeout的理解存在偏差。所以大概说一下误解:
// setTimeout作为一个任务分发器,这个函数会立即执行,而它所要分发的任务,也就是它的第一个参数,才是延迟执行
  • 来自不同任务源的任务会进入到不同的任务队列。其中setTimeout与setInterval是同源的。
  • 其中每一个任务的执行,无论是macro-task还是micro-task,都是借助函数调用栈来完成。

事件循环执行循序

事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后,本轮循环结束。下一轮循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。

当我们在执行setTimeout任务中遇到setTimeout时,它仍然会将对应的任务分发到setTimeout队列中去,但是该任务就得等到下一轮事件循环执行。

那么整个事件循环中何时进行ui render呢?

<div id="div">
    begin
</div>
setTimeout(function() {
    // 应该是这里执行前开始渲染ui,试试用alert阻塞下。
    alert(' ui 已经渲染完毕了吗? ');
    console.log('timeout1');
})

new Promise(function(resolve) {
    console.log('promise1');
    for(var i = 0; i < 1000; i++) {
        i == 99 && resolve();
    }
    console.log('promise2');
}).then(function() {
    console.log('then1');
    alert(' ui 开始渲染 ');
})

console.log('global1');

div.innerHTML = 'end';

上述代码中修改了div的内容,那么在执行那句js代码之后渲染引擎开始修改div的内容呢?

根据HTML Standard,一轮事件循环执行结束之后,下轮事件循环执行之前开始进行UI render。即:macro-task任务执行完毕,接着执行完所有的micro-task任务后,此时本轮循环结束,开始执行UI render。UI render完毕之后接着下一轮循环。

在chrome浏览器中执行以上代码,控制台先输出promise1,promise2,global1,then1(micro-task任务输出),弹出'ui 开始渲染'警告框,点击确定之后,页面中的'begin'变为'end',再弹出警告框'ui 已经渲染完毕了吗?' ,点击确认之后再输入timeout1.

再来一个稍微复杂一点的例子
<div class="outer" style="width:200px;height:200px;background-color: #ccc">
    1
    <div class="inner" style="width:100px;height:100px;background-color: #ddd">begin</div>
</div>
// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');

var i = 0;

// Let's listen for attribute changes on the
// outer element
new MutationObserver(function() {
    console.log('mutate');
}).observe(outer, {
    attributes: true
});

// Here's a click listener…
function onClick() {
    i++;

    if(i === 1) {
        inner.innerHTML = 'end';
    }

    console.log('click');

    setTimeout(function() {
        alert('锚点');
        console.log('timeout');
    }, 0);

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


    outer.setAttribute('data-random', Math.random());
}

// …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);

当我们点击 inner div 时程序依次的执行顺序是:

  1. onclick 入 JS stack
  2. 打印出 click
  3. 将 timeout 压入到 macrotask
  4. 将 promise 压入到 microtask
  5. 修改 outer 属性 data-random
  6. 将 mutate 压入到 microtask,
  7. onclick 出 JS stack

此时,由于用户点击事件onclick产生的macrotask执行完毕,JS stack 清空,开始执行microtask.

  1. promise 入 JS stack
  2. 打印出 promise
  3. promise 出 JS stack
  4. mutate 入 JS stack
  5. 打印出 mutate
  6. mutate 出 JS stack

此时,microtask 执行完毕,JS stack 清空,但是由于事件冒泡,接着执行outer上的onclick事件.

  1. onclick 入 JS stack
  2. 打印出 click
  3. 将 timeout 压入到 macrotask
  4. 将 promise 压入到 microtask
  5. 修改 outer 属性 data-random
  6. 将 mutate 压入到 microtask,
  7. onclick 出 JS stack

此时,由于outer上的onclick事件产生的macrotask执行完毕,JS stack 清空,开始执行microtask.

  1. promise 入 JS stack
  2. 打印出 promise
  3. promise 出 JS stack
  4. mutate 入 JS stack
  5. 打印出 mutate
  6. mutate 出 JS stack

此时,本轮事件循环结束,UI 开始 render.

  1. 页面中inner的innerHTML变为end

此时,UI render 完毕,开始下一轮事件循环.

  1. timeout 入 JS stack
  2. 弹出警告 锚点.
  3. 打印出 timeout
  4. timeout 出 JS stack
  5. timeout 入 JS stack
  6. 弹出警告 锚点.
  7. 打印出 timeout
  8. timeout 出 JS stack

到此为止,整个事件执行完毕,我们可以看到在弹出警告框之前inner的内容已经改变

那如果不是用户点击事件触发onclick,而是js触发呢?
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
inner.click();

此时的执行顺序是:

  1. 首先是script(整体代码)入 JS stack
  2. onclick 入 JS stack
  3. 打印出 click
  4. 将 timeout 压入到 macrotask
  5. 将 promise 压入到 microtask
  6. 修改 outer 属性 data-random
  7. 将 mutate 压入到 microtask,
  8. onclick 出 JS stack

此时,inner 的 onclick 已经出 JS stack,但是script(整体代码)还没有出 JS stack,还不能执行microtask,由于冒泡,接着执行 outer 的 onclick.

  1. onclick 入 JS stack
  2. 打印出 click
  3. 将 timeout 压入到 macrotask
  4. 将 promise 压入到 microtask
  5. 修改 outer 属性 data-random

接着执行的outer.setAttribute('data-random', Math.random());,但是由于上一个mutation microtask还处于等待状态,不能再添加mutation microtask,所以这里不会将 mutate 压入到 microtask。接着执行:

  1. onclick 出 JS stack
  2. script(整体代码)出 JS stack

此时,inner.click()执行完毕,script(整体代码)已出 JS stack,JS stack 清空,开始执行mircotask.

  1. promise 入 JS stack
  2. 打印出 promise
  3. promise 出 JS stack
  4. mutate 入 JS stack
  5. 打印出 mutate
  6. mutate 出 JS stack
  7. promise 入 JS stack
  8. 打印出 promise
  9. promise 出 JS stack

此时,所有的mircotask执行完毕,本轮事件循环结束,UI 开始 render.

  1. 页面中inner的innerHTML变为end

此时,UI render 完毕,开始下一轮事件循环.

  1. timeout 入 JS stack
  2. 弹出警告 锚点.
  3. 打印出 timeout
  4. timeout 出 JS stack
  5. timeout 入 JS stack
  6. 弹出警告 锚点.
  7. 打印出 timeout
  8. timeout 出 JS stack

到此为止,整个事件执行完毕,我们可以看到在弹出警告框之前inner的内容已经改变

总结:首先执行macrotask,当js stack为空时执行microtask,接着开始UI render,接着再开始下一轮循环

参考文献:
深入核心,详解事件循环机制
Tasks, microtasks, queues and schedules

查看原文

lea_ 赞了文章 · 2018-07-02

vue antV G2-3.X组件化

从网上看到 阿里系的图表 antv 觉得非常不错,就想整合到vue中使用。参考了Vuejs2.X组件化-阿里的G2图表组件

// 2019.03.30更新
附上 G2的快速上手 供大家参考

// 2019.04.24
重亲测试了一遍 demo没有问题

安装

npm install @antv/g2 --save

创建vue组件 components/G2Line.vue

<template>
  <div :id="id"></div>
</template>

<script>
import G2 from '@antv/g2'
export default {
  data () {
    return {
      chart: null
    }
  },
  props: {
    charData: {
      type: Array,
      default: function () {
        return {
          data: []
        }
      }
    },
    id: String
  },
  // 如果使用serverData传过来的静态数据 请使用mounted()方法 并注释掉watch
  mounted () {
    this.drawChart()
  },
  // 监听API接口传过来的数据 使用watch  2018-08-21更新
  // watch: {
      // charData: function (val, oldVal) {    // 监听charData,当发生变化时,触发这个回调函数绘制图表
      // console.log('new: %s, old: %s', val, oldVal);
      // this.drawChart(val);
    // }
  },
  methods: {
    drawChart: function () {
      // 2019.03.30 更新 destory方法已被废弃
      // this.chart && this.chart.destory()
      this.chart = new G2.Chart({
        container: this.id,
        width: 600,
        height: 300
      })
      this.chart.source(this.charData)
      this.chart.scale('value', {
        min: 0
      })
      this.chart.scale('year', {
        range: [0, 1]
      })
      this.chart.tooltip({
        crosshairs: {
          type: 'line'
        }
      })
      this.chart.line().position('year*value')
      this.chart.point().position('year*value').size(4).shape('circle').style({
        stroke: '#fff',
        lineWidth: 1
      })
      this.chart.render()
    }
  }
}
</script>

修改HelloWorld.vue 引用组件

<template>
  <div>
    <g2-line :charData="serverData" :id="'c1'"></g2-line>
  </div>
</template>

<script>
import G2Line from './G2Line.vue'
export default {
  components: {
    G2Line
  },
  data () {
    return {
      serverData: [{
        year: '2010',
        value: 3
      }, {
        year: '2011',
        value: 4
      }, {
        year: '2012',
        value: 3.5
      }, {
        year: '2013',
        value: 5
      }, {
        year: '2014',
        value: 4.9
      }, {
        year: '2015',
        value: 6
      }, {
        year: '2016',
        value: 7
      }, {
        year: '2017',
        value: 9
      }, {
        year: '2018',
        value: 13
      }]
    }
  },
  methods: {
    // 此处省略从服务器获取数据并且赋值给this.serverData
    // 推荐使用axios请求接口
  }
}
</script>

效果

clipboard.png

查看原文

赞 6 收藏 6 评论 24

lea_ 赞了文章 · 2018-07-02

vue antV G2-3.X组件化

从网上看到 阿里系的图表 antv 觉得非常不错,就想整合到vue中使用。参考了Vuejs2.X组件化-阿里的G2图表组件

// 2019.03.30更新
附上 G2的快速上手 供大家参考

// 2019.04.24
重亲测试了一遍 demo没有问题

安装

npm install @antv/g2 --save

创建vue组件 components/G2Line.vue

<template>
  <div :id="id"></div>
</template>

<script>
import G2 from '@antv/g2'
export default {
  data () {
    return {
      chart: null
    }
  },
  props: {
    charData: {
      type: Array,
      default: function () {
        return {
          data: []
        }
      }
    },
    id: String
  },
  // 如果使用serverData传过来的静态数据 请使用mounted()方法 并注释掉watch
  mounted () {
    this.drawChart()
  },
  // 监听API接口传过来的数据 使用watch  2018-08-21更新
  // watch: {
      // charData: function (val, oldVal) {    // 监听charData,当发生变化时,触发这个回调函数绘制图表
      // console.log('new: %s, old: %s', val, oldVal);
      // this.drawChart(val);
    // }
  },
  methods: {
    drawChart: function () {
      // 2019.03.30 更新 destory方法已被废弃
      // this.chart && this.chart.destory()
      this.chart = new G2.Chart({
        container: this.id,
        width: 600,
        height: 300
      })
      this.chart.source(this.charData)
      this.chart.scale('value', {
        min: 0
      })
      this.chart.scale('year', {
        range: [0, 1]
      })
      this.chart.tooltip({
        crosshairs: {
          type: 'line'
        }
      })
      this.chart.line().position('year*value')
      this.chart.point().position('year*value').size(4).shape('circle').style({
        stroke: '#fff',
        lineWidth: 1
      })
      this.chart.render()
    }
  }
}
</script>

修改HelloWorld.vue 引用组件

<template>
  <div>
    <g2-line :charData="serverData" :id="'c1'"></g2-line>
  </div>
</template>

<script>
import G2Line from './G2Line.vue'
export default {
  components: {
    G2Line
  },
  data () {
    return {
      serverData: [{
        year: '2010',
        value: 3
      }, {
        year: '2011',
        value: 4
      }, {
        year: '2012',
        value: 3.5
      }, {
        year: '2013',
        value: 5
      }, {
        year: '2014',
        value: 4.9
      }, {
        year: '2015',
        value: 6
      }, {
        year: '2016',
        value: 7
      }, {
        year: '2017',
        value: 9
      }, {
        year: '2018',
        value: 13
      }]
    }
  },
  methods: {
    // 此处省略从服务器获取数据并且赋值给this.serverData
    // 推荐使用axios请求接口
  }
}
</script>

效果

clipboard.png

查看原文

赞 6 收藏 6 评论 24

lea_ 发布了文章 · 2018-07-01

被讨论难了的this关键字

this关键字

this是个常用的关键字,它被自动生成在函数作用域里.并代表函数执行环境的上下文.
如果在开发中,不了解this绑定在那个对象上,会很容易出现bug.

this绑定的规则

默认绑定

当函数独立使用,(没有对象属性的引用进行调用).
this会默认绑定在全局对象undefined.

var log = function () {
    console.log(this)
}
log() //window

使用严格模式
"use strict";
var log = function () {
    console.log(this)
}
log() //undefined

隐形绑定

当函数被某个对象的属性引用后调用.
this会绑定在该对象上.

var obj = {
    log: function () {
        console.log(this);
    }
}
obj.log() //obj

显性绑定

当函数调用时,指定函数的this绑定对象.
涉及的方法有callapplybind.

var log = function () {
    console.log(this)
}
var target = {}

var bindLog = log.bind(target)
bindLog() //target
log.call(target) //target
log.apply(target) //target
显性绑定的优先级比隐形绑定高
function log() {
    console.log(this.name)
}

var visibleObj = {
    name: 'visible'
}

var hideObj = {
    name: 'hide',
    log: log
}

hideObj.log.call(visibleObj) //visible

new绑定

当实例一个构造函数时.
构造函数的this会绑定在实例上.

function construct(tag) {
    this.tag = tag;
}

var instance = new construct('instance')
console.log(instance.tag) //instance
查看原文

赞 2 收藏 2 评论 1

lea_ 关注了用户 · 2018-06-20

奇舞团 @qiwutuan

360最大前端团队,专注技术,更专注生活~

关注 34

认证与成就

  • 获得 35 次点赞
  • 获得 6 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 6 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-12-17
个人主页被 1.1k 人浏览