es6、7、8、9新语法新特性-总结
栏目: JavaScript · 发布时间: 5年前
1、let和const命令
let
命令,用来声明变量。它的用法类似于 var
,但是所声明的变量,只在 let
命令所在的代码块内有效 。
{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1复制代码
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415; PI // 3.1415 PI = 3; // TypeError: Assignment to constant variable.复制代码
2、变量的解构赋值
基本用法
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。
(1)数组的解构赋值
let [foo, [[bar], baz]] = [1, [[2], 3]]; foo // 1 bar // 2 baz // 3 let [ , , third] = ["foo", "bar", "baz"]; third // "baz" let [x, , y] = [1, 2, 3]; x // 1 y // 3 let [head, ...tail] = [1, 2, 3, 4]; head // 1 tail // [2, 3, 4] let [x, y, ...z] = ['a']; x // "a" y // undefined z // []复制代码
(2)对象的解构赋值
。
let { bar, foo } = { foo: "aaa", bar: "bbb" }; foo // "aaa" bar // "bbb" let { baz } = { foo: "aaa", bar: "bbb" }; baz // undefined复制代码
(3)字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象
const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o"复制代码
(4)数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。
let {toString: s} = 123; s === Number.prototype.toString // true let {toString: s} = true; s === Boolean.prototype.toString // true复制代码
(5)函数参数的解构赋值
[[1, 2], [3, 4]].map(([a, b]) => a + b); // [ 3, 7 ]复制代码
function add([x, y]){ return x + y; } add([1, 2]); // 3复制代码
3、字符串的扩展
API:
- codePointAt():能够正确处理4个字节储存的字符,返回一个字符的码点
- String.fromCodePoint():用于从码点返回对应字符,但是这个方法不能识别32位的UTF-16字符
-
at():返回字符串给定位置的字符。该方法不能识别码点大于
0xFFFF
的字 - normalize():用来将字符的不同表示方法统一为同样的形式,这称为Unicode正规化。
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在源字符串的头部
- endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。
-
repeat():返回一个新字符串,表示将原字符串重复
n
次。 -
padStart()
padEnd():
如果某个字符串不够指定长度,会在头部或尾部补全。
padStart
用于头部补全,padEnd
用于尾部补全。
(1)字符的Unicode表示法
只要将码点放入大括号,就能正确解读该字符。
"\u{20BB7}" // " " "\u{41}\u{42}\u{43}" // "ABC" let hello = 123; hell\u{6F} // 123 '\u{1F680}' === '\uD83D\uDE80' // true复制代码
(2)字符串的遍历器接口
ES6为字符串添加了遍历器接口,使得字符串可以被 for...of
循环遍历。
for (let codePoint of 'foo') { console.log(codePoint) } // "f" // "o" // "o" 复制代码
(3)模板字符串
实例代码如下,一看便明了:
// 普通字符串 `In JavaScript '\n' is a line-feed.` // 多行字符串 `In JavaScript this is not legal.` console.log(`string text line 1 string text line 2`); // 字符串中嵌入变量 var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?`复制代码
4、正则的扩展
(1)RegExp构造函数
RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
new RegExp(/abc/ig, 'i').flags // "i"复制代码
(2)字符串的正则方法
字符串对象共有4个方法,可以使用正则表达式: match()
、 replace()
、 search()
和 split()
。
ES6将这4个方法,在语言内部全部调用RegExp的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp对象上。
-
String.prototype.match
调用RegExp.prototype[Symbol.match]
-
String.prototype.replace
调用RegExp.prototype[Symbol.replace]
-
String.prototype.search
调用RegExp.prototype[Symbol.search]
-
String.prototype.split
调用RegExp.prototype[Symbol.split]
(3)u修饰符
- 点字符
-
Unicode字符表示法
-
量词
-
预定义模式
- i修饰符
5、ES6 数值的扩展
二进制和八进制表示法
ES6提供了二进制和八进制数值的新的写法,分别用前缀 0b
(或 0B
)和 0o
(或 0O
)表示。
API
- Number.isFinite()
- Number.isNaN()
- Number.parseInt()
- Number.parseFloat()
- Number.isInteger()
- Number.EPSILON
6、数组的扩展
Array.from()
用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)
Array.of()
Array.of
方法用于将一组值,转换为数组。
7、函数的扩展
ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') { console.log(x, y); } log('Hello') // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello复制代码
(1)箭头函数
ES6允许使用“箭头”( =>
)定义函数。
var f = v => v; // 等于 var f = function(v) { return v; }; 复制代码
(2)扩展运算符
扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1, 2, 3]) // 1 2 3 console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5 [...document.querySelectorAll('div')] // [<div>, <div>, <div>]复制代码
8、对象的扩展
(1)属性和方法的简洁表示法
ES6允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
var foo = 'bar'; var baz = {foo}; baz // {foo: "bar"} // 等同于 var baz = {foo: foo};复制代码
(2)方法的简洁表示
var o = { method() { return "Hello!"; } }; // 等同于 var o = { method: function() { return "Hello!"; } };复制代码
(3)Object.assign
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
var target = { a: 1 }; var source1 = { b: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}复制代码
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
9、Symbol
Symbol值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
10、Promise对象
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise对象有以下两个特点。
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的处。
基本用法
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。
下面代码创造了一个Promise实例。
var promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } });复制代码
Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。
promise.then(function(value) { // success }, function(error) { // failure });复制代码
下面是异步加载图片的例子。
function loadImageAsync(url) { return new Promise(function(resolve, reject) { var image = new Image(); image.onload = function() { resolve(image); }; image.onerror = function() { reject(new Error('Could not load image at ' + url)); }; image.src = url; }); }复制代码
11、set和map数据结构
Set基本用法
S6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成Set数据结构
var s = new Set(); [2, 3, 5, 4, 5, 2, 2].map(x => s.add(x)); for (let i of s) { console.log(i); } // 2 3 5 4复制代码
Map结构的目的和基本用法
ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。
var m = new Map(); var o = {p: 'Hello World'}; m.set(o, 'content') m.get(o) // "content" m.has(o) // true m.delete(o) // true m.has(o) // false复制代码
12、Proxy 和 Reflect
Proxy
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
Proxy
Reflect
对象与 Proxy
对象一样,也是ES6为了操作对象而提供的新API。 Reflect
对象的设计目的有这样几个。
(1) 将 Object
对象的一些明显属于语言内部的方法(比如 Object.defineProperty
),放到 Reflect
对象上。现阶段,某些方法同时在 Object
和 Reflect
对象上部署,未来的新方法将只部署在 Reflect
对象上。
(2) 修改某些Object方法的返回结果,让其变得更合理。比如, Object.defineProperty(obj, name, desc)
在无法定义属性时,会抛出一个错误,而 Reflect.defineProperty(obj, name, desc)
则会返回 false
。
// 老写法 try { Object.defineProperty(target, property, attributes); // success } catch (e) { // failure } // 新写法 if (Reflect.defineProperty(target, property, attributes)) { // success } else { // failure }复制代码
13、Iterator和for...of循环
terator(遍历器)的概念
JavaScript原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令 for...of
循环,Iterator接口主要供 for...of
消费。
Iterator的遍历过程是这样的。
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的 next
方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的 next
方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的 next
方法,直到它指向数据结构的结束位置。
每一次调用 next
方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含 value
和 done
两个属性的对象。其中, value
属性是当前成员的值, done
属性是一个布尔值,表示遍历是否结束。
下面是一个模拟 next
方法返回值的例子。
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}; } }; }复制代码
for...of循环
ES6 借鉴 C++、 Java 、C# 和 Python 语言,引入了 for...of
循环,作为遍历所有数据结构的统一的方法。
一个数据结构只要部署了 Symbol.iterator
属性,就被视为具有iterator接口,就可以用 for...of
循环遍历它的成员。也就是说, for...of
循环内部调用的是数据结构的 Symbol.iterator
方法。
for...of
循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如 arguments
对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。
数组原生具备 iterator
接口(即默认部署了 Symbol.iterator
属性), for...of
循环本质上就是调用这个接口产生的遍历器,可以用下面的代码证明。
const arr = ['red', 'green', 'blue']; for(let v of arr) { console.log(v); // red green blue } const obj = {}; obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr); for(let v of obj) { console.log(v); // red green blue }复制代码
14、Generator/yield
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同
Generator函数有多种理解角度。从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。
执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。
形式上,Generator函数是一个普通函数,但是有两个特征。一是, function
关键字与函数名之间有一个星号;二是,函数体内部使用 yield
语句,定义不同的内部状态(yield语句在英语里的意思就是“产出”)。
function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator();复制代码
上面代码定义了一个Generator函数 helloWorldGenerator
,它内部有两个 yield
语句“hello”和“world”,即该函数有三个状态:hello,world和return语句(结束执行)。
然后,Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用 next
方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个 yield
语句(或 return
语句)为止。换言之,Generator函数是分段执行的, yield
语句是暂停执行的标记,而 next
方法可以恢复执行。
hw.next() // { value: 'hello', done: false } hw.next() // { value: 'world', done: false } hw.next() // { value: 'ending', done: true } hw.next() // { value: undefined, done: true }复制代码
上面代码一共调用了四次 next
方法。
第一次调用,Generator函数开始执行,直到遇到第一个 yield
语句为止。 next
方法返回一个对象,它的 value
属性就是当前 yield
语句的值hello, done
属性的值false,表示遍历还没有结束。
第二次调用,Generator函数从上次 yield
语句停下的地方,一直执行到下一个 yield
语句。 next
方法返回的对象的 value
属性就是当前 yield
语句的值world, done
属性的值false,表示遍历还没有结束。
第三次调用,Generator函数从上次 yield
语句停下的地方,一直执行到 return
语句(如果没有return语句,就执行到函数结束)。 next
方法返回的对象的 value
属性,就是紧跟在 return
语句后面的表达式的值(如果没有 return
语句,则 value
属性的值为undefined), done
属性的值true,表示遍历已经结束。
第四次调用,此时Generator函数已经运行完毕, next
方法返回对象的 value
属性为undefined, done
属性为true。以后再调用 next
方法,返回的都是这个值。
总结一下,调用Generator函数,返回一个遍历器对象,代表Generator函数的内部指针。以后,每次调用遍历器对象的 next
方法,就会返回一个有着 value
和 done
两个属性的对象。 value
属性表示当前的内部状态的值,是 yield
语句后面那个表达式的值; done
属性是一个布尔值,表示是否遍历结束
yield语句
由于Generator函数返回的遍历器对象,只有调用 next
方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。 yield
语句就是暂停标志。
遍历器对象的 next
方法的运行逻辑如下。
(1)遇到 yield
语句,就暂停执行后面的操作,并将紧跟在 yield
后面的那个表达式的值,作为返回的对象的 value
属性值。
(2)下一次调用 next
方法时,再继续往下执行,直到遇到下一个 yield
语句。
(3)如果没有再遇到新的 yield
语句,就一直运行到函数结束,直到 return
语句为止,并将 return
语句后面的表达式的值,作为返回的对象的 value
属性值。
(4)如果该函数没有 return
语句,则返回的对象的 value
属性值为 undefined
。
需要注意的是, yield
语句后面的表达式,只有当调用 next
方法、内部指针指向该语句时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
function* gen() { yield 123 + 456; }复制代码
上面代码中,yield后面的表达式 123 + 456
,不会立即求值,只会在 next
方法将指针移到这一句时,才会求值。
yield
语句与 return
语句既有相似之处,也有区别。相似之处在于,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到 yield
,函数暂停执行,下一次再从该位置继续向后执行,而 return
语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个) return
语句,但是可以执行多次(或者说多个) yield
语句。正常函数只能返回一个值,因为只能执行一次 return
;Generator函数可以返回一系列的值,因为可以有任意多个 yield
。从另一个角度看,也可以说Generator生成了一系列的值,这也就是它的名称的来历(在英语中,generator这个词是“生成器”的意思)
15、async与await
ES7提供了 async
函数,使得异步操作变得更加方便。 async
函数是什么?一句话, async
函数就是Generator函数的语法糖。
依次读取两个文件。
var fs = require('fs'); var readFile = function (fileName) { return new Promise(function (resolve, reject) { fs.readFile(fileName, function(error, data) { if (error) reject(error); resolve(data); }); }); }; var gen = function* (){ var f1 = yield readFile('/etc/fstab'); var f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); };复制代码
写成 async
函数,就是下面这样
var asyncReadFile = async function (){ var f1 = await readFile('/etc/fstab'); var f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); }复制代码
一比较就会发现, async
函数就是将Generator函数的星号( *
)替换成 async
,将 yield
替换成 await
,仅此而已。
async
函数对 Generator 函数的改进,体现在以下四点
(1)内置执行器。Generator函数的执行必须靠执行器,所以才有了 co
模块,而 async
函数自带执行器。也就是说, async
函数的执行,与普通函数一模一样,只要一行。
(2)更好的语义。 async
和 await
,比起星号和 yield
,语义更清楚了。 async
表示函数里有异步操作, await
表示紧跟在后面的表达式需要等待结果。
(3)更广的适用性。 co
模块约定, yield
命令后面只能是Thunk函数或Promise对象,而 async
函数的 await
命令后面,可以是Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
(4)返回值是Promise。 async
函数的返回值是Promise对象,这比Generator函数的返回值是Iterator对象方便多了。你可以用 then
方法指定下一步的操作。
进一步说, async
函数完全可以看作多个异步操作,包装成的一个Promise对象,而 await
命令就是内部 then
命令的语法糖。
16、Class
对熟悉Java,object-c,c#等纯面向对象语言的开发者来说,都会对class有一种特殊的情怀。ES6 引入了class(类),让JavaScript的面向对象编程变得更加简单和易于理解。
class Animal { // 构造函数,实例化的时候将会被调用,如果不指定,那么会有一个不带参数的默认构造函数. constructor(name,color) { this.name = name; this.color = color; } // toString 是原型对象上的属性 toString() { console.log('name:' + this.name + ',color:' + this.color); } } var animal = new Animal('dog','white');//实例化Animal animal.toString(); console.log(animal.hasOwnProperty('name')); //true console.log(animal.hasOwnProperty('toString')); // false console.log(animal.__proto__.hasOwnProperty('toString')); // true class Cat extends Animal { constructor(action) { // 子类必须要在constructor中指定super 函数,否则在新建实例的时候会报错. // 如果没有置顶consructor,默认带super函数的constructor将会被添加、 super('cat','white'); this.action = action; } toString() { console.log(super.toString()); } } var cat = new Cat('catch') cat.toString(); // 实例cat 是 Cat 和 Animal 的实例,和Es5完全一致。 console.log(cat instanceof Cat); // true console.log(cat instanceof Animal); // true复制代码
17、模块化(Module)
ES5不支持原生的模块化,在ES6中模块作为重要的组成部分被添加进来。模块的功能主要由 export 和 import 组成。每一个模块都有自己单独的作用域,模块之间的相互调用关系是通过 export 来规定模块对外暴露的接口,通过import来引用其它模块提供的接口。同时还为模块创造了命名空间,防止函数的命名冲突。
导出(export)
ES6允许在一个模块中使用export来导出多个变量或函数。
导出变量/常量
//test.js export var name = 'Rainbow' 复制代码
ES6将一个文件视为一个模块,上面的模块通过 export 向外输出了一个变量。一个模块也可以同时往外面输出多个变量。
//test.js var name = 'Rainbow'; var age = '24'; export {name, age}; 复制代码
导出函数
// myModule.js export function myModule(someArg) { return someArg; } 复制代码
导入(import)
定义好模块的输出以后就可以在另外一个模块通过import引用。
import {myModule} from 'myModule';// main.js import {name,age} from 'test';// test.js 复制代码
18、修饰器(Decorator)
修饰器(Decorator)是一个函数,用来修改类的行为。这是ES7的一个 提案 ,目前Babel转码器已经支持。
修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。
类的修饰
function testable(target) { target.isTestable = true; } @testable class MyTestableClass {} console.log(MyTestableClass.isTestable) // true复制代码
上面代码中, @testable
就是一个修饰器。它修改了 MyTestableClass
这个类的行为,为它加上了静态属性 isTestable
。
基本上,修饰器的行为就是下面这样。
@decorator class A {} // 等同于 class A {} A = decorator(A) || A;复制代码
方法的修饰
class Person { @readonly name() { return `${this.first} ${this.last}` } }复制代码
参考
ECMAScript® 2016 语言标准
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 还看不懂同事代码?快来补一波 Java 7 语法特性
- Swift语法快速入门(一)之 Swift基础语法
- 在ES6中使用扩展语法有什么好处?它与rest语法有什么不同?
- Python 基础语法
- go语法
- JPQL 语言语法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。