避免全局变量

在计算机编程中,全局变量指的是在所有作用域中都能访问的变量。全局变量是一种不好的实践,因为它会导致一些问题,比如一个已经存在的方法和全局变量的覆盖,当我们不知道变量在哪里被定义的时候,代码就变得很难理解和维护了。在
ES6 中可以利用 let关键字来声明本地变量,好的 JavaScript
代码就是没有定义全局变量的。在 JavaScript
中,我们有时候会无意间创建出全局变量,即如果我们在使用某个变量之前忘了进行声明操作,那么该变量会被自动认为是全局变量,譬如:

function sayHello(){ hello = “Hello World”; return hello; } sayHello();
console.log(hello);

1
2
3
4
5
6
function sayHello(){
  hello = "Hello World";
return hello;
}
sayHello();
console.log(hello);

在上述代码中因为我们在使用 sayHello 函数的时候并没有声明 hello
变量,因此其会创建作为某个全局变量。如果我们想要避免这种偶然创建全局变量的错误,可以通过强制使用
strict
mode
来禁止创建全局变量。

添加事件

于是,我们开始添加事件了,这里添加一个点击遮盖层关闭整个组件的功能,这里有个问题是,我们点击遮盖层事实上关闭的是遮盖以及loading两个标签,而我们这里的isShow属性便派上了用处,我们现在page中设置下属性:

<ui-loading is-show=”{{isLoadingShow}}”></ui-loading>

1
<ui-loading is-show="{{isLoadingShow}}"></ui-loading>

onShow: function() { this.setData({ isLoadingShow: ” }); },

1
2
3
4
5
onShow: function() {
  this.setData({
    isLoadingShow: ”
  });
},

然后我们改造mask以及loading添加事件:

<view class=”cm-overlay” style=”z-index: {{zIndex}}; display:
{{isShow}}” bindtap=”onTap”> </view>

1
2
<view class="cm-overlay" style="z-index: {{zIndex}}; display: {{isShow}}" bindtap="onTap">
</view>

let LayerView = require(‘behavior-layer-view’) Component({ behaviors:
[LayerView], data: { myData: {} }, attached: function () {
console.log(‘mask’) }, methods: { onTap: function() {
this.triggerEvent(‘customevent’, {}, {}) } } })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let LayerView = require(‘behavior-layer-view’)
 
Component({
  behaviors: [LayerView],
 
  data: {
    myData: {}
  },
  attached: function () {
    console.log(‘mask’)
  },
  methods: {
    onTap: function() {
      this.triggerEvent(‘customevent’, {}, {})
    }
  }
})

<ui-mask z-index=”{{maskzIndex}}” is-show=”{{isShow}}”
bindcustomevent=”onMaskEvent”></ui-mask> <view
class=”spinner” style=”z-index: {{meIndex}}; display: {{isShow}}; “>
<view class=”spinner-container container1″> <view
class=”circle1″></view> <view
class=”circle2″></view> <view
class=”circle3″></view> <view
class=”circle4″></view> </view> <view
class=”spinner-container container2″> <view
class=”circle1″></view> <view
class=”circle2″></view> <view
class=”circle3″></view> <view
class=”circle4″></view> </view> <view
class=”spinner-container container3″> <view
class=”circle1″></view> <view
class=”circle2″></view> <view
class=”circle3″></view> <view
class=”circle4”></view> </view> </view>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<ui-mask z-index="{{maskzIndex}}" is-show="{{isShow}}" bindcustomevent="onMaskEvent"></ui-mask>
<view class="spinner" style="z-index: {{meIndex}}; display: {{isShow}}; ">
  <view class="spinner-container container1">
    <view class="circle1"></view>
    <view class="circle2"></view>
    <view class="circle3"></view>
    <view class="circle4"></view>
  </view>
  <view class="spinner-container container2">
    <view class="circle1"></view>
    <view class="circle2"></view>
    <view class="circle3"></view>
    <view class="circle4"></view>
  </view>
  <view class="spinner-container container3">
    <view class="circle1"></view>
    <view class="circle2"></view>
    <view class="circle3"></view>
    <view class="circle4"></view>
  </view>
</view>

const util = require(‘../utils/util.js’); let LayerView =
require(‘behavior-layer-view’); Component({ behaviors: [LayerView],
data: { maskzIndex: util.getBiggerzIndex(), meIndex:
util.getBiggerzIndex() }, attached: function () { console.log(‘loading’)
}, methods: { onMaskEvent: function (e) { console.log(e); this.setData({
isShow: ‘none’ }); } } })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const util = require(‘../utils/util.js’);
let LayerView = require(‘behavior-layer-view’);
 
Component({
  behaviors: [LayerView],
 
  data: {
    maskzIndex: util.getBiggerzIndex(),
    meIndex: util.getBiggerzIndex()
  },
  attached: function () {
    console.log(‘loading’)
  },
  methods: {
    onMaskEvent: function (e) {
      console.log(e);
      this.setData({
        isShow: ‘none’
      });
    }
  }
})

这个时候,当我们点击遮盖层的时候,我们整个组件便关闭了。

变量声明与赋值

ES6 为我们引入了 let 与 const
两种新的变量声明关键字,同时也引入了块作用域;本文首先介绍 ES6
中常用的三种变量声明方式,然后讨论了 JavaScript
按值传递的特性以及多种的赋值方式,最后介绍了复合类型拷贝的技巧。

函数作用域

定义在某个函数内的变量即从属于当前函数作用域,在每次函数调用中都会创建出新的上下文;换言之,我们可以在不同的函数中定义同名变量,这些变量会被绑定到各自的函数作用域中:

// Global Scope function someFunction() { // Local Scope #1 function
someOtherFunction() { // Local Scope #2 } } // Global Scope function
anotherFunction() { // Local Scope #3 } // Global Scope

1
2
3
4
5
6
7
8
9
10
11
12
13
// Global Scope
function someFunction() {
    // Local Scope #1
function someOtherFunction() {
        // Local Scope #2
    }
}
 
// Global Scope
function anotherFunction() {
    // Local Scope #3
}
// Global Scope

函数作用域的缺陷在于粒度过大,在使用闭包或者其他特性时导致异常的变量传递:

var callbacks = []; // 这里的 i 被提升到了当前函数作用域头部 for (var
i = 0; i <= 2; i++) { callbacks[i] = function () { return i * 2;
}; } console.log(callbacks[0]()); //6 console.log(callbacks[1]());
//6 console.log(callbacks[2]()); //6

1
2
3
4
5
6
7
8
9
10
11
12
var callbacks = [];
 
// 这里的 i 被提升到了当前函数作用域头部
for (var i = 0; i <= 2; i++) {
    callbacks[i] = function () {
return i * 2;
        };
}
 
console.log(callbacks[0]()); //6
console.log(callbacks[1]()); //6
console.log(callbacks[2]()); //6

总结

我们今天花了很多功夫写一个loading,发现小程序中的组件事实上是标签,我们没法使用js获取到我们“组件”的实例,所以使用上有很大的区别,但是什么都不能阻碍我们写通用组件的决心,于是我们明天来写一些通用的组件库,并且形成一个小程序的体系,这里想的是有:

① 消息框

② toast提示

③ 日历组件

④ 然后再做一个需要定位的气泡组件

1 赞 收藏
评论

图片 1

DeepCopy: 深拷贝

作用域

作用域(Scope)即代码执行过程中的变量、函数或者对象的可访问区域,作用域决定了变量或者其他资源的可见性;计算机安全中一条基本原则即是用户只应该访问他们需要的资源,而作用域就是在编程中遵循该原则来保证代码的安全性。除此之外,作用域还能够帮助我们提升代码性能、追踪错误并且修复它们。JavaScript
中的作用域主要分为全局作用域(Global Scope)与局部作用域(Local
Scope)两大类,在 ES5
中定义在函数内的变量即是属于某个局部作用域,而定义在函数外的变量即是属于全局作用域。

编写组件

对象

通过解构对象,你可以把它的每个属性与不同的变量绑定,首先指定被绑定的属性,然后紧跟一个要解构的变量。

var robotA = { name: “Bender” }; var robotB = { name: “Flexo” }; var {
name: nameA } = robotA; var { name: nameB } = robotB;
console.log(nameA); // “Bender” console.log(nameB); // “Flexo”

1
2
3
4
5
6
7
8
var robotA = { name: "Bender" };
var robotB = { name: "Flexo" };
var { name: nameA } = robotA;
var { name: nameB } = robotB;
    console.log(nameA);
    // "Bender"
    console.log(nameB);
    // "Flexo"

当属性名与变量名一致时,可以通过一种实用的句法简写:

var { foo, bar } = { foo: “lorem”, bar: “ipsum” }; console.log(foo); //
“lorem” console.log(bar); // “ipsum”

1
2
3
4
5
var { foo, bar } = { foo: "lorem", bar: "ipsum" };
    console.log(foo);
    // "lorem"
    console.log(bar);
    // "ipsum"

与数组解构一样,你可以随意嵌套并进一步组合对象解构:

var complicatedObj = { arrayProp: [ “Zapp”, { second: “Brannigan” } ]
}; var { arrayProp: [first, { second }] } = complicatedObj;
console.log(first); // “Zapp” console.log(second); // “Brannigan”

1
2
3
4
5
6
7
8
9
10
11
var complicatedObj = {
      arrayProp: [
        "Zapp",
        { second: "Brannigan" }
      ]
    };
var { arrayProp: [first, { second }] } = complicatedObj;
    console.log(first);
    // "Zapp"
    console.log(second);
    // "Brannigan"

当你解构一个未定义的属性时,得到的值为undefined:

var { missing } = {}; console.log(missing); // undefined

1
2
3
var { missing } = {};
    console.log(missing);
    // undefined

请注意,当你解构对象并赋值给变量时,如果你已经声明或不打算声明这些变量(亦即赋值语句前没有let、const或var关键字),你应该注意这样一个潜在的语法错误:

{ blowUp } = { blowUp: 10 }; // Syntax error 语法错误

1
2
   { blowUp } = { blowUp: 10 };
    // Syntax error 语法错误

为什么会出错?这是因为JavaScript语法通知解析引擎将任何以{开始的语句解析为一个块语句(例如,{console}是一个合法块语句)。解决方案是将整个表达式用一对小括号包裹:

({ safe } = {}); // No errors 没有语法错误

1
2
   ({ safe } = {});
    // No errors 没有语法错误

函数包裹

为了避免全局变量,第一件事情就是要确保所有的代码都被包在函数中。最简单的办法就是把所有的代码都直接放到一个函数中去:

(function(win) { “use strict”; // 进一步避免创建全局变量 var doc =
window.document; // 在这里声明你的变量 // 一些其他的代码 }(window));

1
2
3
4
5
6
(function(win) {
    "use strict"; // 进一步避免创建全局变量
var doc = window.document;
    // 在这里声明你的变量
    // 一些其他的代码
}(window));

公共组件库

要提高开发效率的第一个前提就是要有足够多的UI组件,小程序本身提供了一些定制化的组件,我们仍然会用到的组件有:

① alert类弹出层

② loading类弹出层

③ 日历组件

④ toast&message类提示弹出组件

⑤ 容器类组件

⑥ ……

之前的做法,是我们将html实体和组件实现直接放到一起,css放到全局global里面去,现在小程序并不支持动态展示wxml,所以动态插入的方式行不通了,我们需要将组件的wxml放到页面里面做预加载,这里我想的是提供一个通用global.ui.wxml文件用以装载所有的wxml实体,常用的组件我们默认全局引入,我们这里先挑点软柿子来捏,我们先实现一个alert类弹出层组件。

我们将原来弹出层类会用到的CSS全部翻译为WXSS,放入global.wxss中:

图片 2

然后我们每个组件都会有一个固定的生命周期:创建->显示->隐藏,这个生命周期是每个组件都具有的特性,所以我们这里应该引入继承概念实现组件,但是小程序官方提供的Components并没有提供继承概念,而是提供了behaviors概念,用以将组件间的公共部分处理掉,所以我们这里也使用behaviors,因为不能操作dom,我们的组件抽象会变得相对简单,不用记录太多dom节点了,另外小程序的组件与我们之前的“组件”从定义到使用上有很大的不同,之前我们是以js作为控制器,现在是以标签wxml作为控制器,根本没有办法在js中获取实例,而小程序组件的生命周期并不包含显示隐藏生命周期,所以他的组件和我们以为的组件有很大的不同

我思考了下为什么小程序中,js不能获取组件的实例,这里得出的结论是:

小程序中所有的WXML必须在页面中进行预加载逻辑,不能动态插入DOM的方式插入WXML,所以小程序没有提供组件实例给我们控制
所以在小程序中想完成组件库,那么便只能把组件做标签使用(而且是js不能获取的标签),而不是js组件,这样会有效帮助我们理解

1
2
小程序中所有的WXML必须在页面中进行预加载逻辑,不能动态插入DOM的方式插入WXML,所以小程序没有提供组件实例给我们控制
所以在小程序中想完成组件库,那么便只能把组件做标签使用(而且是js不能获取的标签),而不是js组件,这样会有效帮助我们理解

我们这里尝试实现一个遮盖层的标签(这里开始不用组件这个词,感觉很有歧义):

图片 3

代码非常简单:

<view class=”cm-overlay”></view>

1
<view class="cm-overlay"></view>

.cm-overlay { background: rgba(0, 0, 0, 0.5); position: fixed; top: 0;
right: 0; bottom: 0; left: 0; }

1
2
3
4
5
6
7
8
.cm-overlay {
    background: rgba(0, 0, 0, 0.5);
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}

let LayerView = require(‘behavior-layer-view’) Component({ behaviors:
[LayerView], data: { myData: {} }, attached: function () { }, methods:
{ } })

1
2
3
4
5
6
7
8
9
10
11
12
let LayerView = require(‘behavior-layer-view’)
 
Component({
  behaviors: [LayerView],
 
  data: {
    myData: {}
  },
  attached: function () { },
  methods: {
  }
})

可以看到,这个遮盖层mask没有什么意义,而且一般来说mask也不会单独存在,一般是一个组件(比如弹出层的loading)会包含一个遮盖层,所以我们这里要改造下Mask的结构,让他可以装载组件,我们从js组件逻辑来说是mask应该是loading的一个实例,但是我们站在标签角度来说,他们两个应该是独立的:

<view class=”cm-overlay”> <slot></slot> </view>

1
2
3
<view class="cm-overlay">
  <slot></slot>
</view>

我们这里实现一个loading的组件(PS:CSS3动画稍微要做点兼容调试):

图片 4

loading样式

.spinner { width: 140rpx; height: 140rpx; position: fixed; align-items:
center; display: flex; top: 50%; left: 50%; margin-left: -70rpx;
margin-top: -70rpx; } .container1 > view, .container2 > view,
.container3 > view { width: 24rpx; height: 24rpx; background-color:
#00b358; border-radius: 100%; position: absolute; -webkit-animation:
bouncedelay 1.2s infinite ease-in-out; animation: bouncedelay 1.2s
infinite ease-in-out; -webkit-animation-fill-mode: both;
animation-fill-mode: both; } .spinner .spinner-container { position:
absolute; width: 66%; height: 66%; top: 10%; left: 10%; } .container2 {
-webkit-transform: rotateZ(45deg); transform: rotateZ(45deg); }
.container3 { -webkit-transform: rotateZ(90deg); transform:
rotateZ(90deg); } .circle1 { top: 0; left: 0; } .circle2 { top: 0;
right: 0; } .circle3 { right: 0; bottom: 0; } .circle4 { left: 0;
bottom: 0; } .container2 .circle1 { -webkit-animation-delay: -1.1s;
animation-delay: -1.1s; } .container3 .circle1 {
-webkit-animation-delay: -1.0s; animation-delay: -1.0s; } .container1
.circle2 { -webkit-animation-delay: -0.9s; animation-delay: -0.9s; }
.container2 .circle2 { -webkit-animation-delay: -0.8s; animation-delay:
-0.8s; } .container3 .circle2 { -webkit-animation-delay: -0.7s;
animation-delay: -0.7s; } .container1 .circle3 {
-webkit-animation-delay: -0.6s; animation-delay: -0.6s; } .container2
.circle3 { -webkit-animation-delay: -0.5s; animation-delay: -0.5s; }
.container3 .circle3 { -webkit-animation-delay: -0.4s; animation-delay:
-0.4s; } .container1 .circle4 { -webkit-animation-delay: -0.3s;
animation-delay: -0.3s; } .container2 .circle4 {
-webkit-animation-delay: -0.2s; animation-delay: -0.2s; } .container3
.circle4 { -webkit-animation-delay: -0.1s; animation-delay: -0.1s; }
@-webkit-keyframes bouncedelay { 0%, 80%, 100% { -webkit-transform:
scale(0.0) } 40% { -webkit-transform: scale(1.0) } } @keyframes
bouncedelay { 0%, 80%, 100% { transform: scale(0.0); -webkit-transform:
scale(0.0); } 40% { transform: scale(1.0); -webkit-transform:
scale(1.0); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
.spinner {
  width: 140rpx;
  height: 140rpx;
  position: fixed;
  align-items: center;
  display: flex;
  top: 50%;
  left: 50%;
  margin-left: -70rpx;
  margin-top: -70rpx;
}
.container1 > view, .container2 > view, .container3 > view {
  width: 24rpx;
  height: 24rpx;
  background-color: #00b358;
  border-radius: 100%;
  position: absolute;
  -webkit-animation: bouncedelay 1.2s infinite ease-in-out;
  animation: bouncedelay 1.2s infinite ease-in-out;
  -webkit-animation-fill-mode: both;
  animation-fill-mode: both;
}
.spinner .spinner-container {
  position: absolute;
  width: 66%;
  height: 66%;
  top: 10%;
  left: 10%;
}
.container2 {
  -webkit-transform: rotateZ(45deg);
  transform: rotateZ(45deg);
}
.container3 {
  -webkit-transform: rotateZ(90deg);
  transform: rotateZ(90deg);
}
.circle1 { top: 0; left: 0; }
.circle2 { top: 0; right: 0; }
.circle3 { right: 0; bottom: 0; }
.circle4 { left: 0; bottom: 0; }
.container2 .circle1 {
  -webkit-animation-delay: -1.1s;
  animation-delay: -1.1s;
}
.container3 .circle1 {
  -webkit-animation-delay: -1.0s;
  animation-delay: -1.0s;
}
.container1 .circle2 {
  -webkit-animation-delay: -0.9s;
  animation-delay: -0.9s;
}
.container2 .circle2 {
  -webkit-animation-delay: -0.8s;
  animation-delay: -0.8s;
}
.container3 .circle2 {
  -webkit-animation-delay: -0.7s;
  animation-delay: -0.7s;
}
.container1 .circle3 {
  -webkit-animation-delay: -0.6s;
  animation-delay: -0.6s;
}
.container2 .circle3 {
  -webkit-animation-delay: -0.5s;
  animation-delay: -0.5s;
}
.container3 .circle3 {
  -webkit-animation-delay: -0.4s;
  animation-delay: -0.4s;
}
.container1 .circle4 {
  -webkit-animation-delay: -0.3s;
  animation-delay: -0.3s;
}
.container2 .circle4 {
  -webkit-animation-delay: -0.2s;
  animation-delay: -0.2s;
}
.container3 .circle4 {
  -webkit-animation-delay: -0.1s;
  animation-delay: -0.1s;
}
 
@-webkit-keyframes bouncedelay {
  0%, 80%, 100% { -webkit-transform: scale(0.0) }
  40% { -webkit-transform: scale(1.0) }
}
@keyframes bouncedelay {
  0%, 80%, 100% {
    transform: scale(0.0);
    -webkit-transform: scale(0.0);
  } 40% {
    transform: scale(1.0);
    -webkit-transform: scale(1.0);
  }
}

<ui-mask z-index=”{{maskzIndex}}” ></ui-mask> <view
class=”spinner” style=”z-index: {{meIndex}}”> <view
class=”spinner-container container1″> <view
class=”circle1″></view> <view
class=”circle2″></view> <view
class=”circle3″></view> <view
class=”circle4″></view> </view> <view
class=”spinner-container container2″> <view
class=”circle1″></view> <view
class=”circle2″></view> <view
class=”circle3″></view> <view
class=”circle4″></view> </view> <view
class=”spinner-container container3″> <view
class=”circle1″></view> <view
class=”circle2″></view> <view
class=”circle3″></view> <view
class=”circle4″></view> </view> </view>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<ui-mask z-index="{{maskzIndex}}" ></ui-mask>
<view class="spinner" style="z-index: {{meIndex}}">
  <view class="spinner-container container1">
    <view class="circle1"></view>
    <view class="circle2"></view>
    <view class="circle3"></view>
    <view class="circle4"></view>
  </view>
  <view class="spinner-container container2">
    <view class="circle1"></view>
    <view class="circle2"></view>
    <view class="circle3"></view>
    <view class="circle4"></view>
  </view>
  <view class="spinner-container container3">
    <view class="circle1"></view>
    <view class="circle2"></view>
    <view class="circle3"></view>
    <view class="circle4"></view>
  </view>
</view>

const util = require(‘../utils/util.js’); let LayerView =
require(‘behavior-layer-view’); Component({ behaviors: [LayerView],
data: { maskzIndex: util.getBiggerzIndex(), meIndex:
util.getBiggerzIndex() }, attached: function () { console.log(‘loading’)
}, methods: { } })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const util = require(‘../utils/util.js’);
let LayerView = require(‘behavior-layer-view’);
 
Component({
  behaviors: [LayerView],
 
  data: {
    maskzIndex: util.getBiggerzIndex(),
    meIndex: util.getBiggerzIndex()
  },
  attached: function () {
    console.log(‘loading’)
  },
  methods: {
  }
})

index调用情况:

<import src=”./mod.searchbox.wxml” /> <view> <template
is=”searchbox” /> <ui-loading></ui-loading> </view>

1
2
3
4
5
6
<import src="./mod.searchbox.wxml" />
 
<view>
  <template is="searchbox" />
  <ui-loading></ui-loading>
</view>

图片 5

我们后续将完整的项目代码放到github上去,这里便继续代码了

Three Dots

函数的生命周期与提升

基础的函数提升同样会将声明提升至作用域头部,不过不同于变量提升,函数同样会将其函数体定义提升至头部;譬如:

function b() { a = 10; return; function a() {} }

1
2
3
4
5
function b() {  
   a = 10;  
return;  
function a() {}
}

会被编译器修改为如下模式:

function b() { function a() {} a = 10; return; }

1
2
3
4
5
function b() {
function a() {}
  a = 10;
return;
}

在内存创建步骤中,JavaScript 解释器会通过 function
关键字识别出函数声明并且将其提升至头部;函数的生命周期则比较简单,声明、初始化与赋值三个步骤都被提升到了作用域头部:

如果我们在作用域中重复地声明同名函数,则会由后者覆盖前者:

sayHello(); function sayHello () { function hello () {
console.log(‘Hello!’); } hello(); function hello () {
console.log(‘Hey!’); } } // Hey!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sayHello();
 
function sayHello () {
function hello () {
        console.log(‘Hello!’);
    }
 
    hello();
 
function hello () {
        console.log(‘Hey!’);
    }
}
 
// Hey!

而 JavaScript 中提供了两种函数的创建方式,函数声明(Function
Declaration)与函数表达式(Function Expression);函数声明即是以
function
关键字开始,跟随者函数名与函数体。而函数表达式则是先声明函数名,然后赋值匿名函数给它;典型的函数表达式如下所示:

var sayHello = function() { console.log(‘Hello!’); }; sayHello(); //
Hello!

1
2
3
4
5
6
7
var sayHello = function() {
  console.log(‘Hello!’);
};
 
sayHello();
 
// Hello!

函数表达式遵循变量提升的规则,函数体并不会被提升至作用域头部:

sayHello(); function sayHello () { function hello () {
console.log(‘Hello!’); } hello(); var hello = function () {
console.log(‘Hey!’); } } // Hello!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sayHello();
 
function sayHello () {
function hello () {
        console.log(‘Hello!’);
    }
 
    hello();
 
var hello = function () {
        console.log(‘Hey!’);
    }
}
 
// Hello!

在 ES5 中,是不允许在块级作用域中创建函数的;而 ES6
中允许在块级作用域中创建函数,块级作用域中创建的函数同样会被提升至当前块级作用域头部与函数作用域头部。不同的是函数体并不会再被提升至函数作用域头部,而仅会被提升到块级作用域头部:

f; // Uncaught ReferenceError: f is not defined (function () { f; //
undefined x; // Uncaught ReferenceError: x is not defined if (true) {
f(); let x; function f() { console.log(‘I am function!’); } } }());

1
2
3
4
5
6
7
8
9
10
11
f; // Uncaught ReferenceError: f is not defined
(function () {
  f; // undefined
  x; // Uncaught ReferenceError: x is not defined
if (true) {
    f();
    let x;
function f() { console.log(‘I am function!’); }
  }
 
}());

微信小程序开发03-这是一个组件

2018/08/02 · 基础技术 ·
小程序

原文出处: 叶小钗   

使用 [].concat 来复制数组

同样类似于对于对象的复制,我们建议使用[].concat来进行数组的深复制:

var list = [1, 2, 3]; var changedList = [].concat(list);
changedList[1] = 2; list === changedList; // false

1
2
3
4
var list = [1, 2, 3];
var changedList = [].concat(list);
changedList[1] = 2;
list === changedList; // false

同样的,concat方法也只能保证一层深复制:

> list = [[1,2,3]] [ [ 1, 2, 3 ] ] > new_list =
[].concat(list) [ [ 1, 2, 3 ] ] > new_list[0][0] = 4 4
> list [ [ 4, 2, 3 ] ]

1
2
3
4
5
6
7
8
> list = [[1,2,3]]
[ [ 1, 2, 3 ] ]
> new_list = [].concat(list)
[ [ 1, 2, 3 ] ]
> new_list[0][0] = 4
4
> list
[ [ 4, 2, 3 ] ]

模块化

另一项开发者用来避免全局变量的技术就是封装到模块 Module
中。一个模块就是不需要创建新的全局变量或者命名空间的通用的功能。不要将所有的代码都放一个负责执行任务或者发布接口的函数中。这里以异步模块定义
Asynchronous Module Definition (AMD) 为例,更详细的 JavaScript
模块化相关知识参考 JavaScript
模块演化简史

//定义 define( “parsing”, //模块名字 [ “dependency1”, “dependency2” ],
// 模块依赖 function( dependency1, dependency2) { //工厂方法 // Instead
of creating a namespace AMD modules // are expected to return their
public interface var Parsing = {}; Parsing.DateParser = function() {
//do something }; return Parsing; } ); // 通过 Require.js 加载模块
require([“parsing”], function(Parsing) { Parsing.DateParser(); //
使用模块 });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//定义
define( "parsing", //模块名字
        [ "dependency1", "dependency2" ], // 模块依赖
        function( dependency1, dependency2) { //工厂方法
 
            // Instead of creating a namespace AMD modules
            // are expected to return their public interface
            var Parsing = {};
            Parsing.DateParser = function() {
              //do something
            };
return Parsing;
        }
);
 
// 通过 Require.js 加载模块
require(["parsing"], function(Parsing) {
    Parsing.DateParser(); // 使用模块
});

1 赞 2 收藏 1
评论

图片 1

基本结构

接上文:微信小程序开发02-小程序基本介绍

我们今天先来实现这个弹出层:

图片 7

之前这个组件是一个容器类组件,弹出层可设置载入的html结构,然后再设置各种事件即可,这种组件有一个特点:

① 只提供Header部分以及容器部分

② 容器部分的HTML结构由业务层提供

③ 容器部分对应样式由业务层提供

我们如果要在小程序中实现这类组件,意味着我们需要往小程序中动态插入WXML结构,我们这里先做个demo,试试往动态插入WXML是不是可行

this.setData({‘wxml’: ` <my-component>
<view>动态插入的节点</view> </my-component> `});

1
2
3
4
5
this.setData({‘wxml’: `
  <my-component>
  <view>动态插入的节点</view>
  </my-component>
`});

图片 8

小程序对应设置的数据进行了转义,所以并不能动态解析,如果站在性能角度思考,不进行动态解析也不是错误的;另一方面,一旦小程序能动态解析wxml,那么可能会涌出各种花式用法,控制力会减低,那么我们这里如何解决这个问题呢?

我想的是,直接将业务级wxml结构放到页面里面,隐藏起来,需要使用弹出层的时候,直接将之装载进去,我们来看看是否可行,我们将我们需要展示的结构放到一个模板当中:

<template name=”searchbox”> <my-component>
<view>动态组件部分</view> </my-component>
</template>

1
2
3
4
5
<template name="searchbox">
  <my-component>
      <view>动态组件部分</view>
  </my-component>
</template>

然后,我们在我们主界面中载入模板:

<import src=”mod.searchbox.wxml”/> <view>
<my-component> <!– 这部分内容将被放置在组件 <slot>
的位置上 –> <view>这里是插入到组件slot中的内容</view>
</my-component> </view> <view> <template
is=”searchbox” /> </view>

1
2
3
4
5
6
7
8
9
10
<import src="mod.searchbox.wxml"/>
<view>
   <my-component>
     <!– 这部分内容将被放置在组件 <slot> 的位置上 –>
      <view>这里是插入到组件slot中的内容</view>
   </my-component>
</view>
<view>
   <template is="searchbox" />
</view>

图片 9

主体结构放到页面中,我们传入数据模型或者控制显示即可,看起来是可行的,于是我们先实现我们基本的样式,因为业务模块的样子应该由业务提供,所以对应样式写到index.wxss里面:

.btn-primary { background-color: #00b358; color: #fff; border: 0 none;
} .btn, .btn-primary, .btn-secondary, .btn-sub { line-height: 88rpx;
height: 88rpx; padding: 0 20rpx; display: inline-block; vertical-align:
middle; text-align: center; border-radius: 8rpx; cursor: pointer;
font-size: 32rpx; -webkit-box-sizing: border-box; box-sizing:
border-box; } .full-width { width: 100%; -webkit-box-sizing: border-box;
box-sizing: border-box; } .c-row { width: auto; display: -webkit-box;
-webkit-box-orient: horizontal; -webkit-box-direction: normal;
-webkit-box-pack: justify; -webkit-box-align: stretch;
-webkit-box-lines: single; display: -webkit-flex;
-webkit-flex-direction: row; -webkit-justify-content: space-between;
-webkit-align-items: strecth; -webkit-align-content: flex-start;
-webkit-flex-wrap: nowrap; padding: 20rpx 40rpx; } .c-span3 { width:
25%; -webkit-box-flex: 3; -webkit-flex: 3 3 auto; } .c-span9 { width:
75%; -webkit-box-flex: 9; -webkit-flex: 9 9 auto; } .search-line {
position: relative; height: 96rpx; line-height: 96rpx; font-size: 30rpx;
font-weight: 600; border-bottom: 1rpx solid #e6e6e6; }
.search-line::after { content: “”; display: inline-block;
vertical-align: middle; width: 20rpx; height: 20rpx; border-top: 4rpx
solid #00b358; border-right: 4rpx solid #00b358; position: absolute;
right: 60rpx; top: 50%; margin-top: -4rpx; -webkit-transform:
rotate(45deg) translateY(-50%); transform: rotate(45deg)
translateY(-50%); -webkit-box-sizing: border-box; box-sizing:
border-box; } .search-line-txt { text-align: right; padding-right:
60rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
.btn-primary {
    background-color: #00b358;
    color: #fff;
    border: 0 none;
}
.btn, .btn-primary, .btn-secondary, .btn-sub {
    line-height: 88rpx;
    height: 88rpx;
    padding: 0 20rpx;
    display: inline-block;
    vertical-align: middle;
    text-align: center;
    border-radius: 8rpx;
    cursor: pointer;
    font-size: 32rpx;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}
.full-width {
    width: 100%;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}
 
 
.c-row {
    width: auto;
    display: -webkit-box;
    -webkit-box-orient: horizontal;
    -webkit-box-direction: normal;
    -webkit-box-pack: justify;
    -webkit-box-align: stretch;
    -webkit-box-lines: single;
    display: -webkit-flex;
    -webkit-flex-direction: row;
    -webkit-justify-content: space-between;
    -webkit-align-items: strecth;
    -webkit-align-content: flex-start;
    -webkit-flex-wrap: nowrap;
    padding: 20rpx 40rpx;
}
 
.c-span3 {
    width: 25%;
    -webkit-box-flex: 3;
    -webkit-flex: 3 3 auto;
}
 
.c-span9 {
    width: 75%;
    -webkit-box-flex: 9;
    -webkit-flex: 9 9 auto;
}
 
.search-line {
    position: relative;
    height: 96rpx;
    line-height: 96rpx;
    font-size: 30rpx;
    font-weight: 600;
    border-bottom: 1rpx solid #e6e6e6;
}
 
.search-line::after {
    content: "";
    display: inline-block;
    vertical-align: middle;
    width: 20rpx;
    height: 20rpx;
    border-top: 4rpx solid #00b358;
    border-right: 4rpx solid #00b358;
    position: absolute;
    right: 60rpx;
    top: 50%;
    margin-top: -4rpx;
    -webkit-transform: rotate(45deg) translateY(-50%);
    transform: rotate(45deg) translateY(-50%);
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}
 
.search-line-txt {
    text-align: right;
    padding-right: 60rpx;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

<template name=”searchbox”> <view class=”c-row search-line”
data-flag=”start”> <view class=”c-span3″> 出发</view>
<view class=”c-span9 js-start search-line-txt”>
请选择出发地</view> </view> <view class=”c-row
search-line” data-flag=”arrive”> <view class=”c-span3″>
到达</view> <view class=”c-span9 js-arrive search-line-txt”>
请选择到达地</view> </view> <view class=”c-row ”
data-flag=”arrive”> <span class=”btn-primary full-width
js_search_list”>查询</span> </view> </template>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template name="searchbox">
  <view class="c-row search-line" data-flag="start">
      <view class="c-span3">
          出发</view>
      <view class="c-span9 js-start search-line-txt">
          请选择出发地</view>
  </view>
  <view class="c-row search-line" data-flag="arrive">
      <view class="c-span3">
          到达</view>
      <view class="c-span9 js-arrive search-line-txt">
          请选择到达地</view>
  </view>
  <view class="c-row " data-flag="arrive">
      <span class="btn-primary full-width js_search_list">查询</span>
  </view>
</template>

图片 10

如此一来,我们基本的弹出层样式就七七八八了,这里可以看出一些特点:小程序与平时我们的样式差距不大,稍微改点就能用,甚至能直接通用;另一方面,我们也需要思考一个问题:公共部分的CSS该怎么处理?其实我这里需要解决的不只是公共的样式部分,还需要解决公共的组件部分。

我这里想的是将所有公共部分的CSS放到一个全局的文件global.wxss中,然后在每个业务级页面import即可,所以我们这里需要形成一个公共的WXSS库,这个与纯web映射起来即可,我们这里便不深入。

延伸阅读

  • 基于 JSX 的动态数据绑定
  • ECMAScript
    2017(ES8)特性概述
  • WebAssembly
    初体验:从零开始重构计算模块

    1 赞 1 收藏
    评论

图片 1

全局作用域

当我们在浏览器控制台或者 Node.js 交互终端中开始编写 JavaScript
时,即进入了所谓的全局作用域:

// the scope is by default global var name = ‘Hammad’;

1
2
// the scope is by default global
var name = ‘Hammad’;

定义在全局作用域中的变量能够被任意的其他作用域中访问:

var name = ‘Hammad’; console.log(name); // logs ‘Hammad’ function
logName() { console.log(name); // ‘name’ is accessible here and
everywhere else } logName(); // logs ‘Hammad’

1
2
3
4
5
6
7
8
9
var name = ‘Hammad’;
 
console.log(name); // logs ‘Hammad’
 
function logName() {
    console.log(name); // ‘name’ is accessible here and everywhere else
}
 
logName(); // logs ‘Hammad’

递归属性遍历

一般来说,在JavaScript中考虑复合类型的深层复制的时候,往往就是指对于Date、Object与Array这三个复合类型的处理。我们能想到的最常用的方法就是先创建一个空的新对象,然后递归遍历旧对象,直到发现基础类型的子节点才赋予到新对象对应的位置。不过这种方法会存在一个问题,就是JavaScript中存在着神奇的原型机制,并且这个原型会在遍历的时候出现,然后原型不应该被赋予给新对象。那么在遍历的过程中,我们应该考虑使用hasOenProperty方法来过滤掉那些继承自原型链上的属性:

function clone(obj) { var copy; // Handle the 3 simple types, and null
or undefined if (null == obj || “object” != typeof obj) return obj; //
Handle Date if (obj instanceof Date) { copy = new Date();
copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj
instanceof Array) { copy = []; for (var i = 0, len = obj.length; i
< len; i++) { copy[i] = clone(obj[i]); } return copy; } // Handle
Object if (obj instanceof Object) { copy = {}; for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]); }
return copy; } throw new Error(“Unable to copy obj! Its type isn’t
supported.”); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function clone(obj) {
var copy;
 
    // Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
 
    // Handle Date
if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
return copy;
    }
 
    // Handle Array
if (obj instanceof Array) {
        copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
return copy;
    }
 
    // Handle Object
if (obj instanceof Object) {
        copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
return copy;
    }
 
throw new Error("Unable to copy obj! Its type isn’t supported.");
}

调用如下:

// This would be cloneable: var tree = { “left” : { “left” : null,
“right” : null, “data” : 3 }, “right” : null, “data” : 8 }; // This
would kind-of work, but you would get 2 copies of the // inner node
instead of 2 references to the same copy var directedAcylicGraph = {
“left” : { “left” : null, “right” : null, “data” : 3 }, “data” : 8 };
directedAcyclicGraph[“right”] = directedAcyclicGraph[“left”]; //
Cloning this would cause a stack overflow due to infinite recursion: var
cylicGraph = { “left” : { “left” : null, “right” : null, “data” : 3 },
“data” : 8 }; cylicGraph[“right”] = cylicGraph;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};
 
// This would kind-of work, but you would get 2 copies of the
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];
 
// Cloning this would cause a stack overflow due to infinite recursion:
var cylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cylicGraph["right"] = cylicGraph;

变量作用域与提升

在 ES6 之前,JavaScript 中只存在着函数作用域;而在 ES6 中,JavaScript
引入了 let、const
等变量声明关键字与块级作用域,在不同作用域下变量与函数的提升表现也是不一致的。在
JavaScript
中,所有绑定的声明会在控制流到达它们出现的作用域时被初始化;这里的作用域其实就是所谓的执行上下文(Execution
Context),每个执行上下文分为内存分配(Memory Creation
Phase)与执行(Execution)这两个阶段。在执行上下文的内存分配阶段会进行变量创建,即开始进入了变量的生命周期;变量的生命周期包含了声明(Declaration
phase)、初始化(Initialization phase)与赋值(Assignment
phase)过程这三个过程。

传统的 var 关键字声明的变量允许在声明之前使用,此时该变量被赋值为
undefined;而函数作用域中声明的函数同样可以在声明前使用,其函数体也被提升到了头部。这种特性表现也就是所谓的提升(Hoisting);虽然在
ES6 中以 let 与 const
关键字声明的变量同样会在作用域头部被初始化,不过这些变量仅允许在实际声明之后使用。在作用域头部与变量实际声明处之间的区域就称为所谓的暂时死域(Temporal
Dead Zone),TDZ 能够避免传统的提升引发的潜在问题。另一方面,由于 ES6
引入了块级作用域,在块级作用域中声明的函数会被提升到该作用域头部,即允许在实际声明前使用;而在部分实现中该函数同时被提升到了所处函数作用域的头部,不过此时被赋值为
undefined。

按值传递

JavaScript
中永远是按值传递(pass-by-value),只不过当我们传递的是某个对象的引用时,这里的值指的是对象的引用。按值传递中函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。而按引用传递(pass-by-reference)时,函数的形参接收实参的隐式引用,而不再是副本。这意味着函数形参的值如果被修改,实参也会被修改。同时两者指向相同的值。我们首先看下
C 中按值传递与引用传递的区别:

void Modify(int p, int * q) { p = 27; // 按值传递 – p是实参a的副本,
只有p被修改 *q = 27; // q是b的引用,q和b都被修改 } int main() { int a =
1; int b = 1; Modify(a, &b); // a 按值传递, b 按引用传递, // a 未变化, b
改变了 return(0); }

1
2
3
4
5
6
7
8
9
10
11
12
13
void Modify(int p, int * q)
{
    p = 27; // 按值传递 – p是实参a的副本, 只有p被修改
    *q = 27; // q是b的引用,q和b都被修改
}
int main()
{
int a = 1;
int b = 1;
    Modify(a, &b);   // a 按值传递, b 按引用传递,
                     // a 未变化, b 改变了
return(0);
}

而在 JavaScript 中,对比例子如下:

function changeStuff(a, b, c) { a = a * 10; b.item = “changed”; c =
{item: “changed”}; } var num = 10; var obj1 = {item: “unchanged”}; var
obj2 = {item: “unchanged”}; changeStuff(num, obj1, obj2);
console.log(num); console.log(obj1.item); console.log(obj2.item); //
输出结果 10 changed unchanged

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}
 
var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
 
changeStuff(num, obj1, obj2);
 
console.log(num);
console.log(obj1.item);    
console.log(obj2.item);
 
// 输出结果
10
changed
unchanged

JavaScript 按值传递就表现于在内部修改了 c 的值但是并不会影响到外部的
obj2 变量。如果我们更深入地来理解这个问题,JavaScript
对于对象的传递则是按共享传递的(pass-by-sharing,也叫按对象传递、按对象共享传递)。最早由Barbara
Liskov.
在1974年的GLU语言中提出;该求值策略被用于Python、Java、Ruby、JS等多种语言。该策略的重点是:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)。
它和按引用传递的不同在于:在共享传递中对函数形参的赋值,不会影响实参的值。按共享传递的直接表现就是上述代码中的
obj1,当我们在函数内修改了 b 指向的对象的属性值时,我们使用 obj1
来访问相同的变量时同样会得到变化后的值。

执行上下文

每个执行上下文又会分为内存创建(Creation Phase)与代码执行(Code
Execution Phase)两个步骤,在创建步骤中会进行变量对象的创建(Variable
Object)、作用域链的创建以及设置当前上下文中的 this 对象。所谓的
Variable Object ,又称为 Activation
Object,包含了当前执行上下文中的所有变量、函数以及具体分支中的定义。当某个函数被执行时,解释器会先扫描所有的函数参数、变量以及其他声明:

‘variableObject’: { // contains function arguments, inner variable and
function declarations }

1
2
3
‘variableObject’: {
    // contains function arguments, inner variable and function declarations
}

在 Variable Object 创建之后,解释器会继续创建作用域链(Scope
Chain);作用域链往往指向其副作用域,往往被用于解析变量。当需要解析某个具体的变量时,JavaScript
解释器会在作用域链上递归查找,直到找到合适的变量或者任何其他需要的资源。作用域链可以被认为是包含了其自身
Variable Object 引用以及所有的父 Variable Object 引用的对象:

‘scopeChain’: { // contains its own variable object and other variable
objects of the parent execution contexts }

1
2
3
‘scopeChain’: {
    // contains its own variable object and other variable objects of the parent execution contexts
}

而执行上下文则可以表述为如下抽象对象:

executionContextObject = { ‘scopeChain’: {}, // contains its own
variableObject and other variableObject of the parent execution contexts
‘variableObject’: {}, // contains function arguments, inner variable and
function declarations ‘this’: valueOfThis }

1
2
3
4
5
executionContextObject = {
    ‘scopeChain’: {}, // contains its own variableObject and other variableObject of the parent execution contexts
    ‘variableObject’: {}, // contains function arguments, inner variable and function declarations
    ‘this’: valueOfThis
}

Object.assign

Object.assign()
方法可以把任意多个的源对象所拥有的自身可枚举属性拷贝给目标对象,然后返回目标对象。Object.assign
方法只会拷贝源对象自身的并且可枚举的属性到目标对象身上。注意,对于访问器属性,该方法会执行那个访问器属性的
getter
函数,然后把得到的值拷贝给目标对象,如果你想拷贝访问器属性本身,请使用
Object.getOwnPropertyDescriptor()
和Object.defineProperties()
方法。

注意,字符串类型和
symbol
类型的属性都会被拷贝。

注意,在属性拷贝过程中可能会产生异常,比如目标对象的某个只读属性和源对象的某个属性同名,这时该方法会抛出一个
TypeError
异常,拷贝过程中断,已经拷贝成功的属性不会受到影响,还未拷贝的属性将不会再被拷贝。

注意, Object.assign 会跳过那些值为
null

undefined
的源对象。

Object.assign(target, …sources)

1
Object.assign(target, …sources)
  • 例子:浅拷贝一个对象

var obj = { a: 1 }; var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }

1
2
3
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
  • 例子:合并若干个对象

var o1 = { a: 1 }; var o2 = { b: 2 }; var o3 = { c: 3 }; var obj =
Object.assign(o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。

1
2
3
4
5
6
7
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
 
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。
  • 例子:拷贝 symbol 类型的属性

var o1 = { a: 1 }; var o2 = { [Symbol(“foo”)]: 2 }; var obj =
Object.assign({}, o1, o2); console.log(obj); // { a: 1,
[Symbol(“foo”)]: 2 }

1
2
3
4
5
var o1 = { a: 1 };
var o2 = { [Symbol("foo")]: 2 };
 
var obj = Object.assign({}, o1, o2);
console.log(obj); // { a: 1, [Symbol("foo")]: 2 }
  • 例子:继承属性和不可枚举属性是不能拷贝的

var obj = Object.create({foo: 1}, { // foo 是个继承属性。 bar: { value:
2 // bar 是个不可枚举属性。 }, baz: { value: 3, enumerable: true // baz
是个自身可枚举属性。 } }); var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }

1
2
3
4
5
6
7
8
9
10
11
12
var obj = Object.create({foo: 1}, { // foo 是个继承属性。
    bar: {
        value: 2  // bar 是个不可枚举属性。
    },
    baz: {
        value: 3,
        enumerable: true  // baz 是个自身可枚举属性。
    }
});
 
var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
  • 例子:原始值会被隐式转换成其包装对象

var v1 = “123”; var v2 = true; var v3 = 10; var v4 = Symbol(“foo”) var
obj = Object.assign({}, v1, null, v2, undefined, v3, v4); //
源对象如果是原始值,会被自动转换成它们的包装对象, // 而 null 和
undefined 这两种原始值会被完全忽略。 //
注意,只有字符串的包装对象才有可能有自身可枚举属性。 console.log(obj);
// { “0”: “1”, “1”: “2”, “2”: “3” }

1
2
3
4
5
6
7
8
9
10
var v1 = "123";
var v2 = true;
var v3 = 10;
var v4 = Symbol("foo")
 
var obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 源对象如果是原始值,会被自动转换成它们的包装对象,
// 而 null 和 undefined 这两种原始值会被完全忽略。
// 注意,只有字符串的包装对象才有可能有自身可枚举属性。
console.log(obj); // { "0": "1", "1": "2", "2": "3" }
  • 例子:拷贝属性过程中发生异常

var target = Object.defineProperty({}, “foo”, { value: 1, writeable:
false }); // target 的 foo 属性是个只读属性。 Object.assign(target,
{bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4}); // TypeError: “foo” is
read-only // 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。
console.log(target.bar); // 2,说明第一个源对象拷贝成功了。
console.log(target.foo2); //
3,说明第二个源对象的第一个属性也拷贝成功了。 console.log(target.foo);
// 1,只读属性不能被覆盖,所以第二个源对象的第二个属性拷贝失败了。
console.log(target.foo3); // undefined,异常之后 assign
方法就退出了,第三个属性是不会被拷贝到的。 console.log(target.baz); //
undefined,第三个源对象更是不会被拷贝到的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var target = Object.defineProperty({}, "foo", {
    value: 1,
    writeable: false
}); // target 的 foo 属性是个只读属性。
 
Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4});
// TypeError: "foo" is read-only
// 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。
 
console.log(target.bar);  // 2,说明第一个源对象拷贝成功了。
console.log(target.foo2); // 3,说明第二个源对象的第一个属性也拷贝成功了。
console.log(target.foo);  // 1,只读属性不能被覆盖,所以第二个源对象的第二个属性拷贝失败了。
console.log(target.foo3); // undefined,异常之后 assign 方法就退出了,第三个属性是不会被拷贝到的。
console.log(target.baz);  // undefined,第三个源对象更是不会被拷贝到的。

发表评论

电子邮件地址不会被公开。 必填项已用*标注