JavaScript 객체의 나머지 및 확산 속성 변경

小云云
풀어 주다: 2018-02-10 10:11:34
원래의
1859명이 탐색했습니다.

ES5에서는 Object.ass(target, [sources])입니다. _.extend(target, [sources])(或者其他选项),在ES2015中引入了Object.assign(target, [sources])

幸运的是,对象的spread语法(ECMAScript第3阶段建议)是如何操作对象,提供了一个简短且易于遵循的语法。

const cat = {  
    legs: 4,
    sound: 'meow'};const dog = {  
    ...cat,
    sound: 'woof'};

console.log(dog);
로그인 후 복사

JavaScript 객체의 나머지 및 확산 속성 변경

在上面的例子中,...catcat对象复制到一个新的dog对象中。.sound属性的值woof放在最后面。

这篇文章我们来了解对象的restspread语法。来看如何实现对象的克隆、合并以及属性重写等。

下面对可枚举属性简短的概括,以及如何区分自己与继承属性。这些是理解对象如何spreadrest语法的必要基础。

枚举和自己属性

在JavaScript中,对象是键(key)和值(value)之间的关联。

key的类型通常是stringsymbol。值可以是一个基本类型(stringbooleannumberundefinednull),也可以是一个objectfunction

下面的对象使用了对象字面符:

const person = {
    name: 'Dave',
    surname: 'Bowman'}
로그인 후 복사

person对象描述了一个人的namesurname

枚举属性

属性(property)有几个属性(attribute)来描述值,还有可写的、可枚举的和可配置的状态。有关于这方面更多的细节,请参阅JavaScript中的对象属性。

可枚举属性是一个布尔值,它指的是在枚举对象属性是否可访问。

可以使用Object.keys()来枚举对象属性(有于访问自己的和可枚举的属性)。也可以使用for...in语句(枚举所有可枚举属性),等等。

对象字面值声明的属性{prop1: 'val1', prop2:'val2'}是可枚举的。让我们看看person对象包含的可枚举的属性:

const keys = Object.keys(person);  
console.log(keys);
로그인 후 복사

JavaScript 객체의 나머지 및 확산 속성 변경

namesurnameperson对象可枚举的属性。

有趣的部分来了。Object spread从源可枚举属性中克隆过来:

console.log({ ...person })
로그인 후 복사

JavaScript 객체의 나머지 및 확산 속성 변경

现在我们给person对象创建一个不可枚举的属性age。然后再看看Objectspread行为:

Object.defineProperty(person, 'age', {  
    enumerable: false, // 属性不可枚举
    value: 25});
console.log(person['age']); // => 25const clone = {  
    ...person
};
console.log(clone);
로그인 후 복사

JavaScript 객체의 나머지 및 확산 속성 변경

namesurname可枚举属从源对象person复制到clone对象中,但是不可枚举属性age并没有被复制进去。

自己的属性

JavaScript原型可以继承。因此对象属性可是以自己的,也可以是继承的。

对象字面符显式声明的属性是自己的,但是对象从原型接收的属性是继承的。

接下来创建一个personB对象,并且设置它的属性为person

const personB = Object.create(person, {  
    profession: {        value: 'Astronaut',        enumerable: true
    }
});

console.log(personB.hasOwnProperty('profession')); // => true  console.log(personB.hasOwnProperty('name'));       // => false  console.log(personB.hasOwnProperty('surname'));    // => false
로그인 후 복사

personB对象有一个自己的属性profession和从person属性中继承过来的namesurname属性。

Object spread从自己的源属性中进行复制的时候,会忽略继承的属性。

const cloneB = {  
    ...personB
};
console.log(cloneB); // => { profession: 'Astronaut' }
로그인 후 복사

...personB对象从源对象personB中仅复制了自己的属性profession,但忽略了可继承的namesurname属性。

Object spread可以从源对象中复制自己的和可枚举的属性。和Object.keys()相同。

传播对象属性

对象字符符中里的Object spread可以复制源对象自己的和可枚举的属性,并将其复制到目标对象中。

const targetObject = {  
    ...sourceObject,
    property: 'Value'};
로그인 후 복사

顺便说一下,在许多方面,Object spread相当于Object.assign()

다행스럽게도 객체에 대한 spread 구문(ECMAScript Phase 3 권장 사항)은 객체 조작 방법에 대한 짧고 따라하기 쉬운 구문을 제공합니다.

const targetObject = Object.assign(  
    { }, 
    sourceObject,
    { property: 'Value' }
);
로그인 후 복사

JavaScript 객체의 나머지 및 확산 속성 변경🎜 🎜위의 예에서 ...catcat 개체를 새로운 dog 개체에 복사합니다. .sound 속성 woof의 값은 끝에 배치됩니다. 🎜🎜이 글에서는 객체의 restspread 구문에 대해 알아봅니다. 객체 복제, 병합, 속성 재작성 등을 구현하는 방법을 살펴보겠습니다. 🎜🎜다음은 열거 가능한 속성에 대한 간략한 요약과 고유 속성과 상속 속성을 구별하는 방법입니다. 이는 객체가 어떻게 확산되고 휴지 구문을 이해하는 데 필요한 기초입니다. 🎜

열거형 및 자체 속성

🎜JavaScript에서 객체는 키(key)와 값(value) 간의 연결입니다. 🎜🎜 유형은 일반적으로 문자열 또는 기호입니다. 값은 기본 유형(문자열, 부울, 숫자, 정의되지 않음 또는 null<)일 수 있습니다. /code> >) 또는 <code>객체 또는 함수일 수 있습니다. 🎜🎜다음 개체는 개체 리터럴을 사용합니다. 🎜
const targetObject = {  
    ...sourceObject1,
    property1: &#39;Value 1&#39;,
    ...sourceObject2,
    ...sourceObject3,
    property2: &#39;Value 2&#39;};
로그인 후 복사
로그인 후 복사
🎜 person 개체는 사람의 이름을 설명합니다. 🎜

열거된 속성

🎜속성(속성)에는 값뿐 아니라 쓰기 가능, 열거 가능 및 구성 가능 상태를 설명하는 여러 가지 속성(속성)이 있습니다. 이에 대한 자세한 내용은 JavaScript의 개체 속성을 참조하세요. 🎜🎜열거 가능한 속성은 열거된 개체의 속성에 액세스할 수 있는지 여부를 나타내는 부울 값입니다. 🎜🎜Object.keys()를 사용하여 객체 속성을 열거할 수 있습니다(자체 및 열거 가능한 속성에 액세스하는 데 유용함). for...in 문(열거 가능한 모든 속성을 열거하기 위해) 등을 사용할 수도 있습니다. 🎜🎜객체 리터럴 {prop1: 'val1', prop2: 'val2'}로 선언된 속성은 열거 가능합니다. person 개체에 포함된 열거 가능한 속성을 살펴보겠습니다. 🎜
const cat = {
    sound: &#39;meow&#39;,
    legs: 4}
로그인 후 복사
로그인 후 복사
🎜JavaScript 객체의 나머지 및 확산 속성 변경🎜🎜namesurnameperson 개체입니다. 열거된 속성을 사용할 수 있습니다. 🎜🎜여기서 재미있는 부분이 나옵니다. 객체 확산소스 열거 가능 속성에서 복제: 🎜
const dog = {  
    ...cat,
    ...{
        sound: &#39;woof&#39; // <----- 覆盖 cat.sound
    }
};console.log(dog);  // => { sound: &#39;woof&#39;, legs: 4 }
로그인 후 복사
로그인 후 복사
🎜JavaScript 객체의 나머지 및 확산 속성 변경🎜🎜이제 person 개체에 대해 열거할 수 없는 age 속성을 ​​만듭니다. 그런 다음 객체확산 동작을 살펴보세요. 🎜
const anotherDog = {  
    ...cat,
    sound: &#39;woof&#39; // <---- Overwrites cat.sound};console.log(anotherDog); // => { sound: &#39;woof&#39;, legs: 4 }
로그인 후 복사
로그인 후 복사
🎜JavaScript 객체의 나머지 및 확산 속성 변경🎜🎜namesurname은 열거 가능한 소스 객체입니다 personclone 객체에 복사되지만 열거할 수 없는 속성인 age는 복사되지 않습니다. 🎜

자체 속성

🎜JavaScript 프로토타입은 상속될 수 있습니다. 따라서 개체 속성은 고유하거나 상속될 수 있습니다. 🎜🎜객체 리터럴에 의해 명시적으로 선언된 속성은 고유하지만 객체가 프로토타입에서 받는 속성은 상속됩니다. 🎜🎜다음으로 personB 개체를 만들고 해당 속성을 person으로 설정합니다. 🎜
const stillCat = {  
    ...{
        sound: &#39;woof&#39; // <---- Is overwritten by cat.sound
    },    ...cat};console.log(stillCat); // => { sound: &#39;meow&#39;, legs: 4 }
로그인 후 복사
로그인 후 복사
🎜 personB 개체에는 고유한 profession< 속성이 있습니다. /code> 및 <code>namesurname 속성은 person 속성에서 상속됩니다. 🎜🎜객체 스프레드가 자체 소스 속성에서 복사할 때 상속된 속성을 무시합니다. 🎜
const bird = {  
    type: &#39;pigeon&#39;,
    color: &#39;white&#39;};const birdClone = {  
    ...bird
};

console.log(birdClone); // => { type: &#39;pigeon&#39;, color: &#39;white&#39; }  console.log(bird === birdClone); // => false
로그인 후 복사
로그인 후 복사
🎜...personB객체는 소스 객체 personB에서 자체 속성 profession만 복사하지만 상속된 속성은 무시합니다. 이름 속성. 🎜
🎜객체 스프레드는 소스 객체에서 고유하고 열거 가능한 속성을 복사할 수 있습니다. Object.keys()와 동일합니다. 🎜

Spread 객체 속성

🎜객체 캐릭터 내 객체 스프레드는 원본 객체의 고유하고 열거 가능한 속성을 복사하여 대상 객체에 복사할 수 있습니다. 🎜
const laptop = {  
    name: &#39;MacBook Pro&#39;,
    screen: {
        size: 17,
        isRetina: true
    }
};const laptopClone = {  
    ...laptop
};

console.log(laptop === laptopClone);               // => false  console.log(laptop.screen === laptopClone.screen); // => true
로그인 후 복사
로그인 후 복사
🎜그런데, 여러 면에서 객체 확산은 Object.sign()과 동일합니다. 위 코드는 다음과 같이 구현할 수도 있습니다: 🎜
const laptopDeepClone = {  
    ...laptop,    screen: {
        ...laptop.screen
    }
};

console.log(laptop === laptopDeepClone);               // => false  console.log(laptop.screen === laptopDeepClone.screen); // => false
로그인 후 복사
로그인 후 복사
🎜 객체 리터럴은 여러 객체 스프레드를 가질 수 있으며 규칙 속성을 사용하여 어떤 조합이라도 선언할 수 있습니다. 🎜
const targetObject = {  
    ...sourceObject1,
    property1: &#39;Value 1&#39;,
    ...sourceObject2,
    ...sourceObject3,
    property2: &#39;Value 2&#39;};
로그인 후 복사
로그인 후 복사

Object spread规则:最后属性获胜

当多个对象被传播,有一些属性具有相同的键时,那么是如何计算最终的值呢?规则很简单:后者扩展属性覆盖具有相同键的早期属性

来看几个示例。下面的对象字面符实例化了一只猫:

const cat = {
    sound: &#39;meow&#39;,
    legs: 4}
로그인 후 복사
로그인 후 복사

让我们扮演Frankenstein博士,把这只猫变成一只狗。注意它的sound属性值:

const dog = {  
    ...cat,
    ...{
        sound: &#39;woof&#39; // <----- 覆盖 cat.sound
    }
};console.log(dog);  // => { sound: &#39;woof&#39;, legs: 4 }
로그인 후 복사
로그인 후 복사

后面的属性值woof覆盖了前面的属性值meow(来自cat对象的sound的值)。这符合使用相同的键值时,后一个属性值将覆盖最早的属性值的规则。

同样的规则也适用于对象初始化的规则属性:

const anotherDog = {  
    ...cat,
    sound: &#39;woof&#39; // <---- Overwrites cat.sound};console.log(anotherDog); // => { sound: &#39;woof&#39;, legs: 4 }
로그인 후 복사
로그인 후 복사

sound: &#39;woof&#39;规则最终获胜,那是因为他在最后。

现在如果你交换传播对象的相对位置,结果是不同的:

const stillCat = {  
    ...{
        sound: &#39;woof&#39; // <---- Is overwritten by cat.sound
    },    ...cat};console.log(stillCat); // => { sound: &#39;meow&#39;, legs: 4 }
로그인 후 복사
로그인 후 복사

猫仍然是猫。尽管第一个源对象提供了sound属性的值为woof,但它还是被后面的cat对象的sound的属性值meow覆盖了。

Object spread的位置和正则性质很重要。这种语法允许实现诸如对象克隆、合并和填充默认值之类的。

下面我们来看看。

对象克隆

使用Object Spread语法可以用一个简短而富有表现力的方式来克隆一个对象。下面的例子克隆了bird对象:

const bird = {  
    type: &#39;pigeon&#39;,
    color: &#39;white&#39;};const birdClone = {  
    ...bird
};

console.log(birdClone); // => { type: &#39;pigeon&#39;, color: &#39;white&#39; }  console.log(bird === birdClone); // => false
로그인 후 복사
로그인 후 복사

.bird在字符符上复制了bird自己和可枚举的属性,并传给了birdClone目标。因此birdClonebird的克隆。

虽然克隆对象技术乍一看似乎很简单,但有一些细节的差异还是需要注意的。

浅拷贝

Object Spread只会做一个对象的浅拷贝。只有对象本身是克隆的,而嵌套的实例不是克隆的。

laptop有一个嵌套的对象screen。如果克隆laptop对象,看看对其嵌套的对象有何影响:

const laptop = {  
    name: &#39;MacBook Pro&#39;,
    screen: {
        size: 17,
        isRetina: true
    }
};const laptopClone = {  
    ...laptop
};

console.log(laptop === laptopClone);               // => false  console.log(laptop.screen === laptopClone.screen); // => true
로그인 후 복사
로그인 후 복사

首先比较laptop === laptopClone,其值是false。主对象被正确克隆。

然而,laptop.screen === laptopClone.screen值是true。这意味着,laptop.screenlaptopClone.screen引用相同的嵌套对象,但没有复制。

其实,你可以在任何级别上做传播。只需稍加努力,就可以克隆嵌套的对象:

const laptopDeepClone = {  
    ...laptop,    screen: {
        ...laptop.screen
    }
};

console.log(laptop === laptopDeepClone);               // => false  console.log(laptop.screen === laptopDeepClone.screen); // => false
로그인 후 복사
로그인 후 복사

一个额外的...laptop.screen就确保了嵌套对象也被克隆了。现在,laptopDeepClone完整的克隆了laptop对象。

JavaScript 객체의 나머지 및 확산 속성 변경

原型丢失

下面的代码片段声明了一个Game的类,并用这个类创建了一个例实例doom

class Game {  
    constructor(name) {        this.name = name;
    }

    getMessage() {        return `I like ${this.name}!`;
    }
}const doom = new Game(&#39;Doom&#39;);  
console.log(doom instanceof Game); // => true  console.log(doom.name);            // => "Doom"  console.log(doom.getMessage());    // => "I like Doom!"
로그인 후 복사

现在,让我们来克隆一个调用构造函数创建的doom实例。这可能会给你带来一个惊喜:

const doomClone = {  
...doom
};

console.log(doomClone instanceof Game); // => false  console.log(doomClone.name);            // => "Doom"  console.log(doomClone.getMessage());    // => TypeError: doomClone.getMessage is not a function
로그인 후 복사

...doom只将自己的属性name复制到doomClone而已。

doomClone是一个普通的JavaScript对象,其原型是Object.prototype,而不是Game.prototype,这是可以预期的。Object Spread不保存源对象的原型

因此,调用doomClone.getMessage()会抛出一个TypeError错误,那是因为doomClone不会继承getMessage()方法。

要修复丢失的原型,需要手动使用__proto__

const doomFullClone = {  
    ...doom,
    __proto__: Game.prototype
};

console.log(doomFullClone instanceof Game); // => true  console.log(doomFullClone.name);            // => "Doom"  console.log(doomFullClone.getMessage());    // => "I like Doom!"
로그인 후 복사

对象字面符上的__proto__确保了doomFullCloneGame.prototype原型。

不赞成使用__proto__,这里只是用来做演示。

对象传播滞后于调用构造函数创建的实例,因为它不保存原型。其意图是用来浅拷贝源对象自己和可枚举的属性。因此忽略原型的方法似乎也是合理的。

顺便说一下,使用Object.assign()可以更合理的克隆doom

const doomFullClone = Object.assign(new Game(), doom);

console.log(doomFullClone instanceof Game); // => true  console.log(doomFullClone.name);            // => "Doom"  console.log(doomFullClone.getMessage());    // => "I like Doom!"
로그인 후 복사

我保证,使用这种方法,原型也会克隆过来。

更新不可变对象

当一个对象在应用程序的多个地方共用时,直接修改这个对象可能会带来意想不到的副作用。而且跟踪这些修改也是极为蛋疼的事情。

更好的方法是使用操作不可变。不可变能更好的控制对象的修改,有利于编写纯函数。即使在一些复杂的场景中,也更容易确定对象更新的源和原因,因为数据流到一个单一的方向。

Object Spread方便以不可变的方式来修改对象。所设你有一个对象描述了一本书的版本信息:

const book = {  
    name: &#39;JavaScript: The Definitive Guide&#39;,
    author: &#39;David Flanagan&#39;,
    edition: 5,
    year: 2008};
로그인 후 복사

然后这本书的第六版本出来了。Object Spread让我们可以以不可变的方式对这个场景进行编程:

const newerBook = {  
    ...book,
    edition: 6,  // <----- Overwrites book.edition
    year: 2011   // <----- Overwrites book.year};

console.log(newerBook);
로그인 후 복사

JavaScript 객체의 나머지 및 확산 속성 변경

...book复制了book对象的属性。然后手动添加edition:6year:2011来更新属性值。

在最后指定重要的属性值,因为相同的键值,后面的会覆盖前面的。

newBook是一个具有更新属性的新对象。与此同时,原来的book对象仍然完好无损。达到我们的不变性需求。

合并对象

合并对象很简单,因为你可以扩展对象任意数量的属性。

让我们来合并三个对象:

const part1 = {  
    color: &#39;white&#39;};const part2 = {  
    model: &#39;Honda&#39;};const part3 = {  
    year: 2005};const car = {  
    ...part1,
    ...part2,
    ...part3
};
console.log(car); // { color: &#39;white&#39;, model: &#39;Honda&#39;, year: 2005 }
로그인 후 복사

car对象的创建是由part1part2part3三个对象合并而来。

不要忘记,后者会覆盖前者的规则。它给出了合并多个具有相同键对象的理由。

让我们改变一下前面的例子。现在part1part3具有一个新的属性configuration

const part1 = {  
    color: &#39;white&#39;,
    configuration: &#39;sedan&#39;};const part2 = {  
    model: &#39;Honda&#39;};const part3 = {  
    year: 2005,
    configuration: &#39;hatchback&#39;};const car = {  
    ...part1,
    ...part2,
    ...part3 // <--- part3.configuration overwrites part1.configuration};
console.log(car);
로그인 후 복사

JavaScript 객체의 나머지 및 확산 속성 변경

首先...part1设置了configuration的值为sedan,但是后面的...part3configuration设置的hatchback覆盖了前面的。

使用默认值填充对象

对象可以在运行时拥有不同的属性集。一些属性可以被设置,另一些可能会丢失。

这种情况可能发生在配置对象的情况下。用户只能指定配置的重要属性,但未能指从默认中提取的属性。

让我们实现一个multiline(str, config)函数,通过给定的宽度,将 str分成多个行。

config对象可能会接受下面几个参数:

  • width:要断开的字符数。默认为10

  • newLine:在行尾添加字符串。默认为\n

  • indent:打算的行。默认值为&#39; &#39;

multiline()函数几个示例:

multiline(&#39;Hello World!&#39;);  
// => &#39;Hello Worl\nd!&#39;multiline(&#39;Hello World!&#39;, { width: 6 });  
// => &#39;Hello \nWorld!&#39;multiline(&#39;Hello World!&#39;, { width: 6, newLine: &#39;*&#39; });  
// => &#39;Hello *World!&#39;multiline(&#39;Hello World!&#39;, { width: 6, newLine: &#39;*&#39;, indent: &#39;_&#39; });  
// => &#39;_Hello *_World!&#39;
로그인 후 복사

config参数接受不同的属性集:你可以表示123个属性,甚至没有属性。

使用Object Spread会非常简单,可以用默认值填充config对象。在对象字面符中首先展开默认对象,然后是config对象:

function multiline(str, config = {}) {  
    const defaultConfig = {
        width: 10,
        newLine: &#39;\n&#39;,
        indent: &#39;&#39;
    };    const safeConfig = {
        ...defaultConfig,
        ...config
    };    let result = &#39;&#39;;    // Implementation of multiline() using    // safeConfig.width, safeConfig.newLine, safeConfig.indent    // ...    return result;
}
로그인 후 복사

让我们管理safeConfig对象字面量。

...defaultConfig从默认值中提取属性,然后...config配置将会覆盖以前的默认值和自定义属性值。

因此,safeConfig具有multiline()函数可以使用的全部属性。无论输入的配置是否会遗漏一些属性,safeConfig都会具备必要的值。

Object Spread能非常直观的使用默认值。

我们需要更进一步

Object Spread非常酷的地方在于可以在嵌套对象上使用。当更新一个大对象时,这是一个很好的优势,具有很好的可读性。但还是推荐使用Object.assign()来替代。

下面的box对象定义了box的标签:

const box = {  
    color: &#39;red&#39;,
    size: {
        width: 200, 
        height: 100 
    },
    items: [&#39;pencil&#39;, &#39;notebook&#39;]
};
로그인 후 복사

box.size描述了box的尺寸,以及box.items中包含了box中可枚举的item

通过增加box.size.height使box变高。只需要在嵌套对象上扩展height属性:

const biggerBox = {  
    ...box,
    size: {
        ...box.size,
        height: 200
    }
};
console.log(biggerBox);
로그인 후 복사

...box可以确保biggerBox接收来自box源的属性。

更新嵌套对象box.sizeheight,只需要额外的一个对象字面量{...box.size, height:200}。这样一来,box.sizeheight属性就得到了一个新值,其值更新为200

我喜欢通过一个语句执行多个更新的可能性。

如何将颜色改为黑色,将宽度增加到400,并添加一个新的项目ruleritems中(使用扩展数组)?这很简单:

const blackBox = {  
    ...box,
    color: &#39;black&#39;,
    size: {
        ...box.size,
        width: 400
    },
    items: [
        ...box.items,        &#39;ruler&#39;
    ]
};
console.log(blackBox);
로그인 후 복사

JavaScript 객체의 나머지 및 확산 속성 변경

传播undefinednull和原始值

当扩展undefinednull或原始值时,不会提取任何属性,也不会抛出任何错误。中会返回一个空的对象:

const nothing = undefined;  
const missingObject = null;  
const two = 2;

console.log({ ...nothing });       // => { }  console.log({ ...missingObject }); // => { }  console.log({ ...two });           // => { }
로그인 후 복사

nothingmissingObjecttwo中,Object Spread没有扩展任何属性。当然,没有理由在原始值上使用Object Spread。

Object rest属性

使用结构赋值将对象的属性提取到变量之后,剩余的属性可以被收集到rest对象中。

这就是对象rest属性的好处:

const style = {  
    width: 300,
    marginLeft: 10,
    marginRight: 30};const { width, ...margin } = style;

console.log(width);  // => 300  console.log(margin); // => { marginLeft: 10, marginRight: 30 }
로그인 후 복사

使用结构性赋值定义了一个新的变量width,并将其值设置为style.width...margin只会收集marginLeftmarginRight属性。

Object rest只收集自己的和可枚举的属性。

注意,Object rest必须是结构性赋值中的最后一个元素。因此const { ...margin , width } = style将会报错:SyntaxError: Rest element must be last element

总结

Object spread有一些规则要记住:

  • 它从源对象中提取自己的和可枚举的属性

  • 扩展的属性具有相同键的,后者会覆盖前者

与此同时,Object spread是简短而且富有表现力的,同时在嵌套对象上也能很好的工作,同时也保持更新的不变性。它可以轻松的实现对象克隆、合并和填充默认属性。

在结构性赋值中使用Object rest语法,可以收集剩余的属性。

实际上,Object rest和Object spread是JavaScript的重要补充。

相关推荐:

了解JavaScript,函数中的 Rest 参数

위 내용은 JavaScript 객체의 나머지 및 확산 속성 변경의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿