JavaScript 深入之创建对象的多种方式以及优缺点

作者:信息技术

JavaScript 深切之创立对象的有余方法乃至优劣点

2017/05/28 · JavaScript · 对象

初稿出处: 冴羽   

出自《JavaScript高档程序设计》

JavaScript 深刻之继续的有余主意和优劣势

2017/05/28 · JavaScript · 继承

原稿出处: 冴羽   

写在日前

这篇小说疏解创设对象的各类办法,以至优劣点。

可是注意:

那篇文章更疑似笔记,因为《JavaScript高等程序设计》写得真是太好了!

  1. 工厂情势

写在头里

本文批注JavaScript各类继承形式和优短处。

只是注意:

那篇文章更疑似笔记,哎,再让自家感叹一句:《JavaScript高端程序设计》写得真是太好了!

1. 厂子情势

function createPerson(name) { var o = new Object(); o.name = name; o.getName = function () { console.log(this.name); }; return o; } var person1 = createPerson('kevin');

1
2
3
4
5
6
7
8
9
10
11
function createPerson(name) {
    var o = new Object();
    o.name = name;
    o.getName = function () {
        console.log(this.name);
    };
 
    return o;
}
 
var person1 = createPerson('kevin');

缺欠:对象不恐怕识别,因为具备的实例都指向一个原型

function createPerson(name) {

1.原型链承袭

function Parent () { this.name = 'kevin'; } Parent.prototype.getName = function () { console.log(this.name); } function Child () { } Child.prototype = new Parent(); var child1 = new Child(); console.log(child1.getName()) // kevin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Parent () {
    this.name = 'kevin';
}
 
Parent.prototype.getName = function () {
    console.log(this.name);
}
 
function Child () {
 
}
 
Child.prototype = new Parent();
 
var child1 = new Child();
 
console.log(child1.getName()) // kevin

问题:

1.援用类型的习性被有着实例分享,举个例子:

function Parent () { this.names = ['kevin', 'daisy']; } function Child () { } Child.prototype = new Parent(); var child1 = new Child(); child1.names.push('yayu'); console.log(child1.names); // ["kevin", "daisy", "yayu"] var child2 = new Child(); console.log(child2.names); // ["kevin", "daisy", "yayu"]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Parent () {
    this.names = ['kevin', 'daisy'];
}
 
function Child () {
 
}
 
Child.prototype = new Parent();
 
var child1 = new Child();
 
child1.names.push('yayu');
 
console.log(child1.names); // ["kevin", "daisy", "yayu"]
 
var child2 = new Child();
 
console.log(child2.names); // ["kevin", "daisy", "yayu"]

2.在开立 Child 的实例时,不可能向Parent传参

2. 构造函数方式

function Person(name) { this.name = name; this.getName = function () { console.log(this.name); }; } var person1 = new Person('kevin');

1
2
3
4
5
6
7
8
function Person(name) {
    this.name = name;
    this.getName = function () {
        console.log(this.name);
    };
}
 
var person1 = new Person('kevin');

亮点:实例能够分辨为七个一定的类型

劣势:每便创造实例时,各种方法都要被创制贰遍

    var o = new Object();

2.借出构造函数(优异三番两次)

function Parent () { this.names = ['kevin', 'daisy']; } function Child () { Parent.call(this); } var child1 = new Child(); child1.names.push('yayu'); console.log(child1.names); // ["kevin", "daisy", "yayu"] var child2 = new Child(); console.log(child2.names); // ["kevin", "daisy"]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Parent () {
    this.names = ['kevin', 'daisy'];
}
 
function Child () {
    Parent.call(this);
}
 
var child1 = new Child();
 
child1.names.push('yayu');
 
console.log(child1.names); // ["kevin", "daisy", "yayu"]
 
var child2 = new Child();
 
console.log(child2.names); // ["kevin", "daisy"]

优点:

1.制止了援引类型的属性被全部实例分享

2.可以在 Child 中向 Parent 传参

比如:

function Parent (name) { this.name = name; } function Child (name) { Parent.call(this, name); } var child1 = new Child('kevin'); console.log(child1.name); // kevin var child2 = new Child('daisy'); console.log(child2.name); // daisy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Parent (name) {
    this.name = name;
}
 
function Child (name) {
    Parent.call(this, name);
}
 
var child1 = new Child('kevin');
 
console.log(child1.name); // kevin
 
var child2 = new Child('daisy');
 
console.log(child2.name); // daisy

缺点:

艺术都在构造函数中定义,每一遍成立实例都会成立二遍方法。

2.1 构造函数方式优化

function Person(name) { this.name = name; this.getName = getName; } function getName() { console.log(this.name); } var person1 = new Person('kevin');

1
2
3
4
5
6
7
8
9
10
function Person(name) {
    this.name = name;
    this.getName = getName;
}
 
function getName() {
    console.log(this.name);
}
 
var person1 = new Person('kevin');

可取:化解了各样方法都要被另行创建的难点

缺陷:那叫什么封装……

    o.name = name;

3.重组承继

原型链承接和杰出一连双剑合璧。

function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Parent.prototype.getName = function () { console.log(this.name) } function Child (name, age) { Parent.call(this, name); this.age = age; } Child.prototype = new Parent(); var child1 = new Child('kevin', '18'); child1.colors.push('black'); console.log(child1.name); // kevin console.log(child1.age); // 18 console.log(child1.colors); // ["red", "blue", "green", "black"] var child2 = new Child('daisy', '20'); console.log(child2.name); // daisy console.log(child2.age); // 20 console.log(child2.colors); // ["red", "blue", "green"]

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
function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
 
Parent.prototype.getName = function () {
    console.log(this.name)
}
 
function Child (name, age) {
 
    Parent.call(this, name);
    
    this.age = age;
 
}
 
Child.prototype = new Parent();
 
var child1 = new Child('kevin', '18');
 
child1.colors.push('black');
 
console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
 
var child2 = new Child('daisy', '20');
 
console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]

亮点:融入原型链承继和构造函数的帮助和益处,是 JavaScript 中最常用的后续方式。

3. 原型方式

function Person(name) { } Person.prototype.name = 'keivn'; Person.prototype.getName = function () { console.log(this.name); }; var person1 = new Person();

1
2
3
4
5
6
7
8
9
10
function Person(name) {
 
}
 
Person.prototype.name = 'keivn';
Person.prototype.getName = function () {
    console.log(this.name);
};
 
var person1 = new Person();

可取:方法不会重新创制

破绽:1. 有所的习性和情势都分享 2. 不可能发轫化参数

    o.getName = function () {

4.原型式承袭

function createObj(o) { function F(){} F.prototype = o; return new F(); }

1
2
3
4
5
function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}

纵然 ES5 Object.create 的衣冠优孟实现,将盛传的指标作为创造的靶子的原型。

缺点:

含蓄援用类型的属性值始终都会分享相应的值,那点跟原型链继承同样。

var person = { name: 'kevin', friends: ['daisy', 'kelly'] } var person1 = createObj(person); var person2 = createObj(person); person1.name = 'person1'; console.log(person2.name); // kevin person1.firends.push('taylor'); console.log(person2.friends); // ["daisy", "kelly", "taylor"]

1
2
3
4
5
6
7
8
9
10
11
12
13
var person = {
    name: 'kevin',
    friends: ['daisy', 'kelly']
}
 
var person1 = createObj(person);
var person2 = createObj(person);
 
person1.name = 'person1';
console.log(person2.name); // kevin
 
person1.firends.push('taylor');
console.log(person2.friends); // ["daisy", "kelly", "taylor"]

注意:修改person1.name的值,person2.name的值并未有发出转移,实际不是因为person1person2有单独的 name 值,而是因为person1.name = 'person1',给person1增加了 name 值,实际不是修改了原型上的 name 值。

3.1 原型形式优化

function Person(name) { } Person.prototype = { name: 'kevin', getName: function () { console.log(this.name); } }; var person1 = new Person();

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name) {
 
}
 
Person.prototype = {
    name: 'kevin',
    getName: function () {
        console.log(this.name);
    }
};
 
var person1 = new Person();

优点:封装性好了好几

症结:重写了原型,错失了constructor属性

        console.log(this.name);

5. 寄生式承接

创设贰个仅用于封装承继进度的函数,该函数在中间以某种方式来做拉长对象,最后回来对象。

function createObj (o) { var clone = object.create(o); clone.sayName = function () { console.log('hi'); } return clone; }

1
2
3
4
5
6
7
function createObj (o) {
    var clone = object.create(o);
    clone.sayName = function () {
        console.log('hi');
    }
    return clone;
}

劣点:跟借用构造函数形式同样,每一遍创设对象都会成立三次方法。

3.2 原型情势优化

function Person(name) { } Person.prototype = { constructor: Person, name: 'kevin', getName: function () { console.log(this.name); } }; var person1 = new Person();

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name) {
 
}
 
Person.prototype = {
    constructor: Person,
    name: 'kevin',
    getName: function () {
        console.log(this.name);
    }
};
 
var person1 = new Person();

亮点:实例能够透过constructor属性找到所属构造函数

症结:原型格局该有的老毛病照旧有

    };

6. 寄生组合式承袭

为了方便大家阅读,在此处再一次一下整合承继的代码:

function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Parent.prototype.getName = function () { console.log(this.name) } function Child (name, age) { Parent.call(this, name); this.age = age; } Child.prototype = new Parent(); var child1 = new Child('kevin', '18'); console.log(child1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
 
Parent.prototype.getName = function () {
    console.log(this.name)
}
 
function Child (name, age) {
    Parent.call(this, name);
    this.age = age;
}
 
Child.prototype = new Parent();
 
var child1 = new Child('kevin', '18');
 
console.log(child1)

结合承继最大的劣势是会调用一次父构造函数。

三遍是设置子类型实例的原型的时候:

Child.prototype = new Parent();

1
Child.prototype = new Parent();

三次在创立子类型实例的时候:

var child1 = new Child('kevin', '18');

1
var child1 = new Child('kevin', '18');

回想下 new 的模拟达成,其实在那句中,大家会实践:

Parent.call(this, name);

1
Parent.call(this, name);

在此处,大家又会调用了三回 Parent 构造函数。

于是,在这一个事例中,假如大家打字与印刷 child1 对象,大家会发掘 Child.prototype 和 child1 都有叁本性能为colors,属性值为['red', 'blue', 'green']

那么大家该怎么立异,制止那三次重复调用呢?

假如我们不行使 Child.prototype = new Parent() ,而是直接的让 Child.prototype 访谈到 Parent.prototype 呢?

看看哪些贯彻:

function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Parent.prototype.getName = function () { console.log(this.name) } function Child (name, age) { Parent.call(this, name); this.age = age; } // 关键的三步 var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F(); var child1 = new Child('kevin', '18'); console.log(child1);

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
function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
 
Parent.prototype.getName = function () {
    console.log(this.name)
}
 
function Child (name, age) {
    Parent.call(this, name);
    this.age = age;
}
 
// 关键的三步
var F = function () {};
 
F.prototype = Parent.prototype;
 
Child.prototype = new F();
 
 
var child1 = new Child('kevin', '18');
 
console.log(child1);

末尾我们封装一下以此三番五次方法:

function object(o) { function F() {} F.prototype = o; return new F(); } function prototype(child, parent) { var prototype = object(parent.prototype); prototype.constructor = child; child.prototype = prototype; } // 当我们选用的时候: prototype(Child, Parent);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}
 
function prototype(child, parent) {
    var prototype = object(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}
 
// 当我们使用的时候:
prototype(Child, Parent);

援用《JavaScript高端程序设计》中对寄生组合式承袭的表彰就是:

这种办法的高功效展示它只调用了一回 Parent 构造函数,並且由此制止了在 Parent.prototype 上边创设不要求的、多余的品质。与此同期,原型链还是能够保证不改变;由此,仍是能够够符合规律使用 instanceof 和 isPrototypeOf。开垦人员布满以为寄生组合式承接是引用类型最优质的接续范式。

4. 整合格局

构造函数情势与原型形式双剑合璧。

function Person(name) { this.name = name; } Person.prototype = { constructor: Person, getName: function () { console.log(this.name); } }; var person1 = new Person();

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name) {
    this.name = name;
}
 
Person.prototype = {
    constructor: Person,
    getName: function () {
        console.log(this.name);
    }
};
 
var person1 = new Person();

亮点:该共享的共享,该民用的个体,使用最常见的方式

劣势:有的人正是指望全体都写在一块儿,即更加好的封装性

    return o;

深远种类

JavaScript深刻连串目录地址:。

JavaScript深远类别测度写十五篇左右,意在帮我们捋顺JavaScript底层知识,重点疏解如原型、功用域、施行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、传承等困难概念。

假设有荒唐只怕不不务空名的地点,请必须给予指正,拾分感激。假使喜欢照旧持有启发,招待star,对小编也是一种鞭挞。

  1. JavaScirpt 深刻之从原型到原型链
  2. JavaScript 深切之词法功效域和动态功用域
  3. JavaScript 深刻之试行上下文栈
  4. JavaScript 深切之变量对象
  5. JavaScript 深切之功力域链
  6. JavaScript 深刻之从 ECMAScript 标准解读 this
  7. JavaScript 深切之执行上下文
  8. JavaScript 深切之闭包
  9. JavaScript 深刻之参数按值传递
  10. JavaScript 深入之call和apply的效仿完成
  11. JavaScript 深刻之bind的一步一趋达成
  12. JavaScript 深远之new的模拟完成
  13. JavaScript 浓密之类数组对象与 arguments
  14. JavaScript 深刻之创设对象的有余格局以致优劣势

    1 赞 3 收藏 评论

图片 1

4.1 动态原型方式

function Person(name) { this.name = name; if (typeof this.getName != "function") { Person.prototype.getName = function () { console.log(this.name); } } } var person1 = new Person();

1
2
3
4
5
6
7
8
9
10
function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype.getName = function () {
            console.log(this.name);
        }
    }
}
 
var person1 = new Person();

小心:使用动态原型形式时,无法用对象字面量重写原型

解释下为啥:

function Person(name) { this.name = name; if (typeof this.getName != "function") { Person.prototype = { constructor: Person, getName: function () { console.log(this.name); } } } } var person1 = new Person('kevin'); var person2 = new Person('daisy'); // 报错 并从未该措施 person1.getName(); // 注释掉上面包车型大巴代码,那句是能够举办的。 person2.getName();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}
 
var person1 = new Person('kevin');
var person2 = new Person('daisy');
 
// 报错 并没有该方法
person1.getName();
 
// 注释掉上面的代码,这句是可以执行的。
person2.getName();

为了表达那些标题,借使开端实行var person1 = new Person('kevin')

只要对 new 和 apply 的平底施行进度不是很熟识,能够阅读尾部相关链接中的小说。

咱俩想起下 new 的落到实处步骤:

  1. 率先新建贰个对象
  2. 接下来将指标的原型指向 Person.prototype
  3. 然后 Person.apply(obj)
  4. 归来那几个目的

只顾那一年,回看下 apply 的落实步骤,会实行 obj.Person 方法,那年就能进行 if 语句里的原委,注意构造函数的 prototype 属性指向了实例的原型,使用字面量形式直接覆盖 Person.prototype,并不会变动实例的原型的值,person1 还是是指向了在此以前的原型,并非 Person.prototype。而在此之前的原型是尚未 getName 方法的,所以就报错了!

借令你就算想用字面量方式写代码,能够尝尝下这种:

function Person(name) { this.name = name; if (typeof this.getName != "function") { Person.prototype = { constructor: Person, getName: function () { console.log(this.name); } } return new Person(name); } } var person1 = new Person('kevin'); var person2 = new Person('daisy'); person1.getName(); // kevin person2.getName(); // daisy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
 
        return new Person(name);
    }
}
 
var person1 = new Person('kevin');
var person2 = new Person('daisy');
 
person1.getName(); // kevin
person2.getName();  // daisy

}

5.1 寄生构造函数方式

function Person(name) { var o = new Object(); o.name = name; o.getName = function () { console.log(this.name); }; return o; } var person1 = new Person('kevin'); console.log(person1 instanceof Person) // false console.log(person1 instanceof Object) // true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(name) {
 
    var o = new Object();
    o.name = name;
    o.getName = function () {
        console.log(this.name);
    };
 
    return o;
 
}
 
var person1 = new Person('kevin');
console.log(person1 instanceof Person) // false
console.log(person1 instanceof Object)  // true

寄生构造函数格局,作者个人感到应当那样读:

寄生-构造函数-格局,也等于说寄生在构造函数的一种办法。

也便是说打着构造函数的品牌挂羊头卖狗肉,你看创制的实例使用 instanceof 都无可奈何指向构造函数!

如此方法可以在特殊情状下利用。比方我们想创造二个具有额外措施的非常数组,不过又不想向来修改Array构造函数,我们可以那样写:

function SpecialArray() { var values = new Array(); for (var i = 0, len = arguments.length; i len; i++) { values.push(arguments[i]); } values.toPipedString = function () { return this.join("|"); }; return values; } var colors = new SpecialArray('red', 'blue', 'green'); var colors2 = SpecialArray('red2', 'blue2', 'green2'); console.log(colors); console.log(colors.toPipedString()); // red|blue|green console.log(colors2); console.log(colors2.toPipedString()); // red2|blue2|green2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function SpecialArray() {
    var values = new Array();
 
    for (var i = 0, len = arguments.length; i  len; i++) {
        values.push(arguments[i]);
    }
 
    values.toPipedString = function () {
        return this.join("|");
    };
    return values;
}
 
var colors = new SpecialArray('red', 'blue', 'green');
var colors2 = SpecialArray('red2', 'blue2', 'green2');
 
 
console.log(colors);
console.log(colors.toPipedString()); // red|blue|green
 
console.log(colors2);
console.log(colors2.toPipedString()); // red2|blue2|green2

你会意识,其实所谓的寄生构造函数形式正是比工厂方式在创建对象的时候,多应用了三个new,实际上两个的结果是平等的。

然则小编恐怕是梦想能像使用普通 Array 一样选拔 SpecialArray,尽管把 SpecialArray 当成函数也大同小异能用,不过那实际不是小编的原意,也变得不文雅。

在能够利用任何方式的情况下,不要采取这种形式。

只是值得说的是,上边例子中的循环:

for (var i = 0, len = arguments.length; i len; i++) { values.push(arguments[i]); }

1
2
3
for (var i = 0, len = arguments.length; i  len; i++) {
    values.push(arguments[i]);
}

可以替换到:

values.push.apply(values, arguments);

1
values.push.apply(values, arguments);

var person1 = createPerson('kevin');

5.2 稳妥构造函数格局

function person(name){ var o = new Object(); o.sayName = function(){ console.log(name); }; return o; } var person1 = person('kevin'); person1.sayName(); // kevin person1.name = "daisy"; person1.sayName(); // kevin console.log(person1.name); // daisy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function person(name){
    var o = new Object();
    o.sayName = function(){
        console.log(name);
    };
    return o;
}
 
var person1 = person('kevin');
 
person1.sayName(); // kevin
 
person1.name = "daisy";
 
person1.sayName(); // kevin
 
console.log(person1.name); // daisy

所谓安妥对象,指的是尚未集体性质,况且其方法也不援引 this 的目的。

与寄生构造函数格局有两点不相同:

  1. 新创建的实例方法不引用 this
  2. 不利用 new 操作符调用构造函数

妥帖对象最契合在一些有惊无险的条件中。

伏贴构造函数格局也跟工厂情势同样,不可能甄别对象所属类型。

瑕玷:对象不能辨别,因为具有的实例都对准二个原型

深深连串

JavaScript深切体系目录地址:。

JavaScript深远连串估量写十五篇左右,目的在于帮大家捋顺JavaScript底层知识,重视批注如原型、成效域、实施上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、承袭等难点概念。

万一有荒唐也许不踏踏实实的地点,请必得给予指正,十二分多谢。假如喜欢依然持有启发,款待star,对小编也是一种驱策。

  1. JavaScirpt 深刻之从原型到原型链
  2. JavaScript 深远之词法功用域和动态作用域
  3. JavaScript 深刻之实践上下文栈
  4. JavaScript 深切之变量对象
  5. JavaScript 深刻之效用域链
  6. JavaScript 深切之从 ECMAScript 标准解读 this
  7. JavaScript 长远之实行上下文
  8. JavaScript 深刻之闭包
  9. JavaScript 深刻之参数按值传递
  10. JavaScript 深刻之call和apply的依样葫芦达成
  11. JavaScript 深远之bind的效仿完结
  12. JavaScript 深切之new的模仿完成
  13. JavaScript 深刻之类数组对象与 arguments

    1 赞 收藏 评论

图片 2

  1. 构造函数格局

function Person(name) {

    this.name = name;

    this.getName = function () {

        console.log(this.name);

    };

}

var person1 = new Person('kevin');

优点:实例可以识别为四个特定的体系

劣点:每一遍成立实例时,每种方法都要被成立二回

2.1 构造函数格局优化

function Person(name) {

    this.name = name;

    this.getName = getName;

}

function getName() {

    console.log(this.name);

}

var person1 = new Person('kevin');

亮点:化解了每一种方法都要被再一次成立的主题材料

症结:那叫什么封装……

  1. 原型形式

function Person(name) {

}

Person.prototype.name = 'keivn';

Person.prototype.getName = function () {

    console.log(this.name);

};

var person1 = new Person();

优点:方法不会重复创造

症结:1. 具备的性质和方法都分享 2. 不能够起先化参数

3.1 原型格局优化

function Person(name) {

}

Person.prototype = {

    name: 'kevin',

    getName: function () {

        console.log(this.name);

    }

};

var person1 = new Person();

var person1 = new Person();

可取:封装性好了几许

破绽:重写了原型,错过了constructor属性

3.2 原型方式优化

function Person(name) {

}

Person.prototype = {

    constructor: Person,

    name: 'kevin',

    getName: function () {

        console.log(this.name);

    }

};

本文由杏彩发布,转载请注明来源

关键词: