本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

前沿

装饰器

装饰器是一种函数,写成@ + 函数名。它可以放在类和类方法的定义前面。

装饰器的行为

1
2
3
4
5
6
7
@decorator
class A {}

// 等同于

class A {}
A = decorator(A) || A;

装饰器函数的第一个参数,就是所要装饰的目标类。

注意,装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。

添加实例属性

1
2
3
4
5
6
7
8
9
function testable(target) {
target.prototype.isTestable = true;
}

@testable
class MyTestableClass {}

let obj = new MyTestableClass();
obj.isTestable // true

Object.assign()

Object.assign()  方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。

1
2
3
4
5
6
7
8
9
10
11
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };


const returnedTarget = Object.assign(target, source);

console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }

console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
1
Object.assign(target, ...sources)

如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。

[复制一个对象]

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

[深拷贝问题]

针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是(可枚举)属性值。

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
const log = console.log;

function test() {
'use strict';
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
log(JSON.stringify(obj2));
// { a: 0, b: { c: 0}}

obj1.a = 1;
log(JSON.stringify(obj1));
// { a: 1, b: { c: 0}}
log(JSON.stringify(obj2));
// { a: 0, b: { c: 0}}

obj2.a = 2;
log(JSON.stringify(obj1));
// { a: 1, b: { c: 0}}
log(JSON.stringify(obj2));
// { a: 2, b: { c: 0}}

obj2.b.c = 3;
log(JSON.stringify(obj1));
// { a: 1, b: { c: 3}}
log(JSON.stringify(obj2));
// { a: 2, b: { c: 3}}

// Deep Clone
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
log(JSON.stringify(obj3));
// { a: 0, b: { c: 0}}
}

test();

[合并对象]

1
2
3
4
5
6
7
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };

const 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
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };

const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

[拷贝 symbol 类型的属性]

1
2
3
4
5
6
const o1 = { a: 1 };
const o2 = { [Symbol('foo')]: 2 };

const obj = Object.assign({}, o1, o2);
console.log(obj); // { a : 1, [Symbol("foo")]: 2 } (cf. bug 1207182 on Firefox)
Object.getOwnPropertySymbols(obj); // [Symbol(foo)]

[继承属性和不可枚举属性是不能拷贝的]

[原始类型会被包装为对象]

1
2
3
4
5
6
7
8
9
const v1 = "abc";
const v2 = true;
const v3 = 10;
const v4 = Symbol("foo")

const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始类型会被包装,null 和 undefined 会被忽略。
// 注意,只有字符串的包装对象才可能有自身可枚举属性。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }

数组的处理

Object.assign可以用来处理数组,但是会把数组视为对象。

1
2
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]

Object.assign只能进行值的复制,如果要复制的值是一个取值函数,那么将求值后再复制。

1
2
3
4
5
6
7
const source = {
get foo() { return 1 }
};
const target = {};

Object.assign(target, source)
// { foo: 1 }

为对象添加属性

1
2
3
4
5
class Point {
constructor(x, y) {
Object.assign(this, {x, y});
}
}

通过Object.assign方法,将x属性和y属性添加到Point类的对象实例。

克隆对象

1
2
3
function clone(origin) {
return Object.assign({}, origin);
}

将原始对象拷贝到一个空对象,就得到了原始对象的克隆。

合并多个对象

将多个对象合并到某个对象。

1
const merge = (target, ...sources) => Object.assign(target, ...sources);

如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。

1
const merge = (...sources) => Object.assign({}, ...sources);

Object.create()

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

Object.freeze()

Object.freeze()  方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

Object.keys()

Object.keys()  方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// simple array
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

// array like object with random key ordering
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']

// getFoo is a property which isn't enumerable
var myObj = Object.create({}, {
getFoo: {
value: function () { return this.foo; }
}
});
myObj.foo = 1;
console.log(Object.keys(myObj)); // console: ['foo']

方法的装饰

装饰器不仅可以装饰类,还可以装饰类的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function readonly(target, name, descriptor){
// descriptor对象原来的值如下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
descriptor.writable = false;
return descriptor;
}

readonly(Person.prototype, 'name', descriptor);
// 类似于
Object.defineProperty(Person.prototype, 'name', descriptor);

修改属性描述对象的enumerable属性,使得该属性不可遍历。

装饰器有注释的作用。

组件写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component({
tag: 'my-component',
styleUrl: 'my-component.scss'
})
export class MyComponent {
@Prop() first: string;
@Prop() last: string;
@State() isVisible: boolean = true;

render() {
return (
<p>Hello, my name is {this.first} {this.last}</p>
);
}
}
1
2
3
4
5
class Example {
@dec(1)
@dec(2)
method(){}
}

外层装饰器@dec(1)先进入,但是内层装饰器@dec(2)先执行。

装饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。

UEditor 是由百度「FEX前端研发团队」开发的所见即所得富文本web编辑器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE HTML>
<html lang="en-US">

<head>
<meta charset="UTF-8">
<title>ueditor demo</title>
</head>

<body>
<!-- 加载编辑器的容器 -->
<script id="container" name="content" type="text/plain">
这里写你的初始化内容
</script>
<!-- 配置文件 -->
<script type="text/javascript" src="ueditor.config.js"></script>
<!-- 编辑器源码文件 -->
<script type="text/javascript" src="ueditor.all.js"></script>
<!-- 实例化编辑器 -->
<script type="text/javascript">
var ue = UE.getEditor('container');
</script>
</body>

</html>

传入自定义的参数

编辑器有很多可自定义的参数项,在实例化的时候可以传入给编辑器:

1
2
3
var ue = UE.getEditor('container', {
autoHeight: false
});

配置项也可以通过 ueditor.config.js 文件修改

设置和读取编辑器的内容

通 getContent 和 setContent 方法可以设置和读取编辑器的内容

1
2
3
4
5
6
7
8
9
10
var ue = UE.getContent();
//对编辑器的操作最好在编辑器ready之后再做
ue.ready(function() {
//设置编辑器的内容
ue.setContent('hello');
//获取html内容,返回: <p>hello</p>
var html = ue.getContent();
//获取纯文本内容,返回: hello
var txt = ue.getContentTxt();
});

打开控制台(注意:windows系统下请使用管理员权限打开),输入:

1
npm install -g grunt-cli

然后输入以下命令。

1
npm install grunt --save-dev

完整的按钮列表

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
toolbars: [
[
'anchor', //锚点
'undo', //撤销
'redo', //重做
'bold', //加粗
'indent', //首行缩进
'snapscreen', //截图
'italic', //斜体
'underline', //下划线
'strikethrough', //删除线
'subscript', //下标
'fontborder', //字符边框
'superscript', //上标
'formatmatch', //格式刷
'source', //源代码
'blockquote', //引用
'pasteplain', //纯文本粘贴模式
'selectall', //全选
'print', //打印
'preview', //预览
'horizontal', //分隔线
'removeformat', //清除格式
'time', //时间
'date', //日期
'unlink', //取消链接
'insertrow', //前插入行
'insertcol', //前插入列
'mergeright', //右合并单元格
'mergedown', //下合并单元格
'deleterow', //删除行
'deletecol', //删除列
'splittorows', //拆分成行
'splittocols', //拆分成列
'splittocells', //完全拆分单元格
'deletecaption', //删除表格标题
'inserttitle', //插入标题
'mergecells', //合并多个单元格
'deletetable', //删除表格
'cleardoc', //清空文档
'insertparagraphbeforetable', //"表格前插入行"
'insertcode', //代码语言
'fontfamily', //字体
'fontsize', //字号
'paragraph', //段落格式
'simpleupload', //单图上传
'insertimage', //多图上传
'edittable', //表格属性
'edittd', //单元格属性
'link', //超链接
'emotion', //表情
'spechars', //特殊字符
'searchreplace', //查询替换
'map', //Baidu地图
'gmap', //Google地图
'insertvideo', //视频
'help', //帮助
'justifyleft', //居左对齐
'justifyright', //居右对齐
'justifycenter', //居中对齐
'justifyjustify', //两端对齐
'forecolor', //字体颜色
'backcolor', //背景色
'insertorderedlist', //有序列表
'insertunorderedlist', //无序列表
'fullscreen', //全屏
'directionalityltr', //从左向右输入
'directionalityrtl', //从右向左输入
'rowspacingtop', //段前距
'rowspacingbottom', //段后距
'pagebreak', //分页
'insertframe', //插入Iframe
'imagenone', //默认
'imageleft', //左浮动
'imageright', //右浮动
'attachment', //附件
'imagecenter', //居中
'wordimage', //图片转存
'lineheight', //行间距
'edittip ', //编辑提示
'customstyle', //自定义标题
'autotypeset', //自动排版
'webapp', //百度应用
'touppercase', //字母大写
'tolowercase', //字母小写
'background', //背景
'template', //模板
'scrawl', //涂鸦
'music', //音乐
'inserttable', //插入表格
'drafts', // 从草稿箱加载
'charts', // 图表
]
]

传入配置参数

1
2
3
4
5
6
7
var ue = UE.getEditor('container', {
toolbars: [
['fullscreen', 'source', 'undo', 'redo', 'bold']
],
autoHeightEnabled: true,
autoFloatEnabled: true
});

读取配置项

读取配置项可以通过getOpt方法读取

1
var lang = ue.getOpt('lang'); //默认返回:zh-cn

前端配置项说明

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
-   **UEDITOR_HOME_URL** {Path String} [默认值:根据config文件路径自动获取] // 为编辑器实例添加一个路径,这个不能被注释
- **serverUrl** {Path String} [默认值:URL + "php/controller.php"] // 服务器统一请求接口路径
- **toolbars** {2d Array} //工具栏上的所有的功能按钮和下拉框,可以在new编辑器的实例时选择自己需要的从新定义
- **labelMap** {Object} [默认:从lang包的labelMap项获取] //参数格式是键值对,键名对应toolbar参数的项:{"bold": "加粗"} ],当鼠标放在工具栏上时显示的tooltip提示,留空支持自动多语言配置,否则以配置值为准
- **lang** {String} [默认值:"zh-cn"] //lang值也可以通过自动获取 (navigator.language||navigator.browserLanguage ||navigator.userLanguage).toLowerCase(),语言配置项,默认是zh-cn。有需要的话也可以使用如下这样的方式来自动多语言切换,当然,前提条件是lang文件夹下存在对应的语言文件:
- **langPath** {Path String} [默认值:URL +"lang/"] //语言包文件存放目录
- **theme** {String} [默认值:'default'] //主题配置项,默认是default。有需要的话也可以使用如下这样的方式来自动多主题切换,当然,前提条件是themes文件夹下存在对应的主题文件:
- **themePath** {Path String} [默认值:URL +"themes/"] //现有如下皮肤:default
- **zIndex** {Number} [默认值:900] //编辑器在页面上的z-index层级的基数,默认是900
- **charset** {String} [默认值:"utf-8"] //针对getAllHtml方法,会在对应的head标签中增加该编码设置。
- **customDomain** {Boolean} [默认值:false] //若实例化编辑器的页面手动修改的domain,此处需要设置为true
- **isShow** {Boolean} [默认值:true] //默认显示编辑器
- **textarea** {String} [默认值:'editorValue'] // 提交表单时,服务器获取编辑器提交内容的所用的参数,多实例时可以给容器name属性,会将name给定的值最为每个实例的键值,不用每次实例化的时候都设置这个值
- **initialContent** {String} [默认值:'欢迎使用ueditor!'] //初始化编辑器的内容,也可以通过textarea/script给值,看官网例子
- **autoClearinitialContent** {Boolean} [默认值:true] //是否自动清除编辑器初始内容,注意:如果focus属性设置为true,这个也为真,那么编辑器一上来就会触发导致初始化的内容看不到了
- **focus** {Boolean} [默认值:false] //初始化时,是否让编辑器获得焦点true或false
- **initialStyle** {String} [默认值:'p{line-height:1em}']//编辑器层级的基数,可以用来改变字体等 //如果自定义,最好给p标签如下的行高,要不输入中文时,会有跳动感
- **iframeCssUrl** {Path String} [默认值:URL + '/themes/iframe.css'] //给编辑器内部引入一个css文件
- **indentValue** {String} [默认值:'2em'] //首行缩进距离,默认是2em
- **initialFrameWidth** {Number} [默认值:1000] //初始化编辑器宽度,默认1000
- **initialFrameHeight** {Number} [默认值:320] //初始化编辑器高度,默认320
- **readonly** {Boolean} [默认值:false] //编辑器初始化结束后,编辑区域是否是只读的,默认是false
- **autoClearEmptyNode** {Boolean} [默认值:true] //getContent时,是否删除空的inlineElement节点(包括嵌套的情况)
- **enableAutoSave** {Boolean} [默认值:true] //启用自动保存
- **saveInterval** {Number} [默认值:500] //自动保存间隔时间,单位ms
- **imageScaleEnabled** {Boolean} [默认值:true] //启用图片拉伸缩放
- **fullscreen** {Boolean} [默认值:false] //是否开启初始化时即全屏,默认关闭
- **imagePopup** {Boolean} [默认值:true] //图片操作的浮层开关,默认打开
- **autoSyncData** {Boolean} [默认值:true] //自动同步编辑器要提交的数据
- **emotionLocalization** {Boolean} [默认值:false] //是否开启表情本地化,默认关闭。若要开启请确保emotion文件夹下包含官网提供的images表情文件夹
- **retainOnlyLabelPasted** {Boolean} [默认值:false] //粘贴只保留标签,去除标签所有属性
- **pasteplain** {Boolean} [默认值:false] //是否默认为纯文本粘贴。false为不使用纯文本粘贴,true为使用纯文本粘贴
- **filterTxtRules** {Object} //纯文本粘贴模式下的过滤规则

- **allHtmlEnabled** [默认值:false] //提交到后台的数据是否包含整个html字符串
- **insertorderedlist** //有序列表的下拉配置,值留空时支持多语言自动识别,若配置值,则以此值为准
**insertunorderedlist** //无序列表的下拉配置,值留空时支持多语言自动识别,若配置值,则以此值为准

- **listDefaultPaddingLeft** [默认值:'30'//默认的左边缩进的基数倍
- **listiconpath** [默认值:'http://bs.baidu.com/listicon/']//自定义标号的路径
- **maxListLevel** [默认值:3] //限制可以tab的级数, 设置-1为不限制
- **autoTransWordToList** [默认值:false] //禁止word中粘贴进来的列表自动变成列表标签
- **fontfamily** //字体设置 label留空支持多语言自动切换,若配置,则以配置值为准

- **fontsize** {Array} //字号
1
2
//默认值:
[10, 11, 12, 14, 16, 18, 20, 24, 36]
  • paragraph {Object} //段落格式 值留空时支持多语言自动识别,若配置,则以配置值为准

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //默认值:
    {
    'p': '',
    'h1': '',
    'h2': '',
    'h3': '',
    'h4': '',
    'h5': '',
    'h6': ''
    }
  • rowspacingtop {Array} //段间距 值和显示的名字相同

    1
    2
    //默认值:
    ['5', '10', '15', '20', '25']
  • rowspacingbottom //段间距 值和显示的名字相同

    1
    2
    //默认值:
    ['5', '10', '15', '20', '25']
  • lineheight [默认值:[‘1’, ‘1.5’,’1.75’,’2’, ‘3’, ‘4’, ‘5’] ] //行内间距 值和显示的名字相同

  • customstyle [Array] //自定义样式,不支持国际化,此处配置值即可最后显示值block的元素是依据设置段落的逻辑设置的,inline的元素依据BIU的逻辑设置,尽量使用一些常用的标签

    1
    2
    3
    4
    5
    6
    7
    //默认值:
    [{
    tag: 'h1', //tag 使用的标签名字
    name: 'tc', //
    label: '', //label 显示的名字也是用来标识不同类型的标识符,注意这个值每个要不同
    style: 'border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:center;margin:0 0 20px 0;' //style 添加的样式
    }, //每一个对象就是一个自定义的样式
  • enableContextMenu {Boolean} [默认值:true] //打开右键菜单功能

  • contextMenu {Object} //右键菜单的内容

  • shortcutMenu {Array} //快捷菜单

    1
    2
    //默认值
    ["fontfamily", "fontsize", "bold", "italic", "underline", "forecolor", "backcolor", "insertorderedlist", "insertunorderedlist"]
  • elementPathEnabled {Boolean} [默认值:true] //是否启用元素路径,默认是显示

  • wordCount {Boolean} [默认值:true] //是否开启字数统计

  • maximumWords {Number} [默认值:10000] //允许的最大字符数

  • wordCountMsg {String} [默认值:] //当前已输入