区别很明显,值传递是不会改变实际的参数数值,而引用传递是可以改变参数的实际数值的,还有值传递是基本类型的传递,引用传递是对基本类型进行了封装之后再传递。
1、按值传递
值传参针对基本类型,引用传参针对引用类型,传参可以理解为复制变量值。基本类型复制后俩个变量完全独立,之后任何一方改变都不会影响另一方;
let num1 = 5;
let num2 = num1;
console.log(num1,num2); // 5 5
num1 = 6;
console.log(num1,num2); // 6 5
因为是按值传递的,传递完后俩个变量各不相干!
function addTen(num) {
num += 10;
return num;
}
var count = 20;
var result = addTen(count); //按值传递 num = count
alert(count); // 20, 没变化
alert(result); // 30
2、引用传递
引用传递适用于引用类型,object,array,function
引用类型复制的是引用(即指针),之后的任何一方改变都会映射到另一方。
// 创建object对象
const obj1 = {
name: 'zhang',
email: '123@qqcom',
cal: '110'
};
const obj2 = obj1;
console.log(obj1,obj2);
// {name: "zhang", email: "123@qqcom", cal: "110"}
// {name: "zhang", email: "123@qqcom", cal: "110"}
// 对象使用点语法访问内部成员
obj1.name = 'shuai';
console.log(obj1,obj2);
// {name: "shuai", email: "123@qqcom", cal: "110"}
// {name: "shuai", email: "123@qqcom", cal: "110"}
console.log(obj1 === obj2); // true
3、函数传参都是值传递
传参不管是基本类型还是引用类型永远是值传递
function setName(obj) {
obj.name = "Nicholas";
}
var person = new Object();
setName(person); // obj = person
alert(person.name); // "Nicholas" 看起来是按引用传递,但千万不要以为是按引用传递~~~
当 var person = new Object(); 时,可以用下图表示变量和对象的关系:
当调用函数 setName(person); 时,下图可以表示全局变量person和局部变量obj的关心:
以上代码中创建一个对象,并将其保存在变量person中。然后,这个变量被传递到setName(obj)函数中之后就被复制给了obj。在这个函数内部,obj和person引用的是同一个对象。换句话说,即使ECMAScript说这个变量是按值传递的,但obj也会按引用来访问同一个对象。于是,在函数内部为obj添加name属性后,函数外部的person也将有所反应;因为这时的person和obj指向同一个堆内存地址。所以,很多人错误的认为:在局部作用域中修改的对象会在全局对象中反映出来,就说明参数是按引用传递的。
为了证明对象也是按值传递的,我们再来看看下面这个经过修改的例子:
function setName(obj) {
obj.name = "Nicholas";
obj = new Object(); //改变obj的指向,此时obj指向一个新的内存地址,不再和person指向同一个
obj.name = "Greg";
}
var person = new Object();
setName(person); //你看看下面,相信我也是按值传递的了吧
alert(person.name); //"Nicholas"
当创建obj对象 obj = new Object(); 时,来看看这时person和obj的关系图:
这个例子与前一个唯一的区别,就是setName()函数中添加了两行代码: obj = new Object(); 用来改变obj的指向; obj.name = “Greg”; 用来给新创建的obj添加属性。如果是按引用传递的,那么person就会自动被修改为指向新创建的obj的内存地址,则person的name属性值被修改为”Greg”
基本类型参数的传递与基本类型的复制一样,传递的是变量值。
function addTen(num) {
num = num + 10;
return num;
}
var count = 20;
var result = addTen(count);
console.log(count); // 20
console.log(result); // 30
引用类型参数的传递与引用类型的复制一样,传递的是内存地址。
function setName(obj){
obj.name = 'xxx';
obj = {name: 'ppp'}; // obj指向一个新的地址,与person不再指向同一个地址
console.log(obj.name); // 'ppp'
}
const person = {name : 'oo'};
setName(person);
console.log(person.name); // ‘xxx’
官方解释来一发:
ECMAScript中所有函数的参数都是按值传递的。
也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型的传递如同基本类型的复制一样,而引用类型值的传递,如同引用类型变量的复制一样。
总结
很简单,javascript函数参数都是按值传递(都是栈内数据的拷贝)。 基本类型传的是值本身(因为直接把值存在栈内),引用类型传的是对象在内存里面的地址 (因为复杂对象存在堆内,所以在栈里存对象所在的堆地址)。
模板字面量是增强版的字符串,它用反引号标识
模板字面量:将插值表达式嵌入到字符串中
let message = `Hello world!`;
console.log(message); // "Hello world!"
console.log(typeof message); //"string"
console.log(message.length);// 12
模板字面量看上去仅仅是普通JS字符串的升级版,但二者之间真正的区别在于模板字面量的变量占位符。变量占位符允许将任何有效的JS表达式嵌入到模板字面量中,并将其结果输出为字符串的一部分
变量占位符由起始的 ${ 与结束的 }来界定,之间允许放入任意的JS 表达式。最简单的变量占位符允许将本地变量直接嵌入到结果字符串中
普通字符串拼接
let name = "Nicholas",
message = "Hello,"+name;
console.log(message); // "Hello, Nicholas"
使用模板字面量
let name = "Nicholas",
message =`Hello, ${name}`;
console.log(message); // "Hello, Nicholas"
占位符 ${name} 会访问本地变量name,并将其值插入到message字符串中。message变量会立即保留该占位符的结果
既然占位符是JS表达式,那么可替换的就不仅仅是简单的变量名。可以轻易嵌入运算符、函数调用等
let count = 10,
price = 0.25,
message = `${count} items cost $${(count * price).toFixed(2)}.`;
console.log(message); // "10 items cost $2.50."
function fn() {
return "Hello World";
}
console.log(`foo ${fn()} bar`); // foo Hello World bar
利用模板字面量动态生成HTML菜单
注意:利用传统的方式拼接HTML非常麻烦,这里使用模板字面量非常高效,在反引号中可以随意书写HTML代码且有提示和高亮,按照标签结构可随意换行进行显示。
let list = ["汽车", "电脑", "水果"];
let str = "";
list.forEach(function (item) {
str += "<li>" + item + "</li>";
});
console.log(str);
const ul = document.createElement("ul");
ul.innerHTML = str;
document.body.appendChild(ul);
// 创建一个数组存放导航菜单
let menus = ['首页','视频','文章','留言','登录'];
// 利用模板字面量创建html代码
let htmlStr = `
<nav>
<a href="#">${menus[0]}</a>
<a href="#">${menus[1]}</a>
<a href="#">${menus[2]}</a>
<a href="#">${menus[0]}</a>
<a href="#">${menus[4]}</a>
</nav>`;
console.log(htmlStr);
// 将HTML代码插入到body之后
document.body.insertAdjacentHTML('beforeEnd',htmlStr);
模板字面量真正的威力来自于标签模板,每个模板标签都可以执行模板字面量上的转换并返回最终的字符串值。标签指的是在模板字面量第一个反引号前方标注的字符串
let tag = (ms)=>alert(ms);
//传统调用方式
tag('Hello world');
//用标签模板来调用它
tag`Hello world`;
//标签模板:模板字面量前是一个标识符,本质上是一个函数
//所以,我们可以认为标签模板是函数调用的特殊形式
//函数名:模板字面量前面的标识符
//调用参数:标签后面的模板字面量
tag 就是应用到Hello world
模板字面量上的模板标签
标签可以是一个函数,调用时传入加工过的模板字面量各部分数据,但必须结合每个部分来创建结果。第一个参数是一个数组,包含Javascript解释过后的字面量字符串,它之后的所有参数都是每一个占位符的解释值
标签函数通常使用不定参数特性来定义占位符,从而简化数据处理的过程
function tag(literals, ...substitutions) {
// 返回一个字符串
}
为了进一步理解传递给tag函数的参数,查看以下代码
let count = 10,
price = 0.25,
message = passthru`${count} items cost $ ${(count * price).toFixed(2)}.`;
如果有一个名为passthru()的函数,那么作为一个模板字面量标签,它会接受3个参数首先是一个literals数组,包含以下元素
1、第一个占位符前的空字符串(“”)
2、第一、二个占位符之间的字符串(“items cost $”)
3、第二个占位符后的字符串(“.”)
下一个参数是变量count的解释值,传参为10,它也成为了substitutions数组里的第一个元素
最后一个参数是(count*price).toFixed(2)的解释值,传参为2.50,它是substitutions数组里的第二个元素
var a = 5;
var b = 10;
tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);
通过这种模式,我们可以将literals和substitutions两个数组交织在一起重组结果字符串。先取出literals中的首个元素,再取出substitution中的首个元素,然后交替继续取出每一个元素,直到字符串拼接完成。于是可以通过从两个数组中交替取值的方式模拟模板字面量的默认行为
function passthru(literals, ...substitutions) {
let result = ""; // 仅使用 substitution 的元素数量来进行循环
for(let i = 0; i < substitutions.length; i++) {
result +=literals[i];
result +=substitutions[i];
} // 添加最后一个字面量``
result += literals[literals.length - 1];
return result;
}
let count = 10,
price = 0.25,
message = passthru`${count} items cost $${(count * price).toFixed(2)}.`;
console.log(message); // "10 items cost $2.50."
这个示例定义了一个passthru标签,模拟模板字面量的默认行为,展示了一次转换过程。此处的小窍门是使用substitutions.length来为循环计数
下面来举一个简单的栗子,两个数求和
let sum = (strs,...args)=>{
console.log(strs);
// console.log(`${s[0]}+${s[1]}=${s[2]}`);
console.log(`${strs[0]}${args[0]}${strs[1]}${args[1]}${strs[2]}${args[2]}${strs[3]}`);
}
let a=3,b=2;
sum`${a}+${b}=${a+b}`;
这里我们看到了,第一个参数是由所有的字符串字面量组成的数组,后面是插值表达式的值
String.raw方法,往往用来充当模板字面量的处理函数,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,对应于替换变量后的模板字面量
如果原字符串的斜杠已经转义,那么String.raw不会做任何处理
String.raw
Hi\n// "Hi\\n"
“标签模板”的一个重要应用,就是过滤HTML字符串,防止用户输入恶意内容
var message = SaferHTML` ${sender} has sent you a message.`;
function SaferHTML(templateData) {
var s = templateData[0];
for(vari = 1; i<arguments.length; i++) {
var arg = String(arguments[i]); // Escape special characters in the substitution.
s += arg.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">"); // Don't escape special characters in the template.
s +=templateData[i];
}
return s;
}
上面代码中,sender变量往往是用户提供的,经过SaferHTML函数处理,里面的特殊字符都会被转义
var sender = '<script>alert("abc")</script>'; // 恶意代码
var message = SaferHTML`<p>${sender} has sent you a message.</p>`;
console.log(message);//<p><script>alert("abc")</script> has sent you a message.</p>
我们经常定义许多对象和数组,然后有组织地从中提取相关的信息片段。在ES6中添加了可以简化这种任务的新特性:解构。解构是一种打破数据结构,将其拆分为更小部分的过程。
解构赋值是对赋值运算符的扩展。
ES6之前数组的解构
let arr = [1, 2, 3];
let a = arr[0];
let b = arr[1];
let c = arr[2];
基本数组解构
let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
可嵌套数组解构
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3
可忽略
let [, , b] = [1, 2, 3];
// b = 3
let [a, b] = [1, 2, 3];
// a = 1
// b = 2
不完全解构let [a = 1, b] = []; // a = 1, b = undefined
rest剩余运算符
let [a, ...b] = [1, 2, 3,4];
//a = 1
//b = [2, 3,4]
解构字符串
let [a, b, c, d, e] = 'hello';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'o'
举个栗子:两个数交换
普通交换操作,借助第三个变量t
let a = 5;
let b = 20;
let t;
console.log("a=%d,b=%d",a,b); // a=5,b=20
t = a;
a = b;
b = t;
console.log("a=%d,b=%d",a,b); // a=20,b=5
利用数组解构赋值进行交换
let a = 5;
let b = 20;
console.log("a=%d,b=%d",a,b); // a=5,b=20
[a,b] = [b,a];
console.log("a=%d,b=%d",a,b); // a=20,b=5
之前获取对象中的数据
let item = {id: 10, name: "手机",price: 1899};
let id = item.id;
let name = item.name;
let price = item.price;
let node = {
type: "Identifier",
name: "foo"
};
let { type, name } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
在这段代码中,node.type的值被存储在名为type的变量中;node.name的值被存储在名为name的变量中
({ type="Literal", name='zhang' } = {type: "Identifier",name: "foo"});
console.log(type); // "Identifier"
console.log(name); // "foo"
[注意] 一定要用一对小括号包裹解构赋值语句,JS引擎将一对开放的花括号视为一个代码块。语法规定,代码块语句不允许出现在赋值语句左侧,添加小括号后可以将块语句转化为一个表达式,从而实现整个解构赋值过程
传统方式获取参数
let setUser = function (id, userInfo) {
//要求第二个参数必须是一个对象
userInfo = userInfo || {};
let name = userInfo.name;
let email = userInfo.email;
let status = userInfo.status;
return { id, name, email, status };
};
let user = new setUser(1);
user = new setUser(1, {
name: "admin",
email: "admin@qq.com",
status: true,
});
console.dir(user);
解构参数进行简化
setUser = function (id, { name, email, status }) {
return { id, name, email, status };
};
user = new setUser(1, {
name: "wang",
email: "wang@qq.com",
status: true,
});
console.dir(user);
//user = setUser(3);会报错
//在解构中禁止使用undefined,null来初始化
//let { x, y } = null;
//let { x, y } = undefined;
setUser = function (id, { name, email, status } = {}) {
return { id, name, email, status };
};
console.log(setUser(3));
let user = {
userName : "zhang",
userEmail : "zhang@qq.com",
getInfo: function(){
return `姓名:${this.userName},邮箱:${this.userEmail}`;
}
};
user.getInfo();
let {userName,userEmail} = user;
console.log(userName,userEmail);
// 进行简化
user = {
userName,
userEmail,
getInfo: function(){
return `姓名:${this.userName},邮箱:${this.userEmail}`;
}
};
在 JavaScript 中,使用底下的方式创建对象字面量应该是屡见不鲜了:
let x,y;
let o = {
x: x,
y: y
}
在ES6中现在可以直接写:
let x,y;
let o = {x,y};
变量名称会成为特性名称,而变量值会成为特性值。当特性实际参考函数时,在过去会这么写:
var o = {
doSome : function() {
//...
},
doOther : function(param) {
// ...
}
};
ES6 可以简单地写为:
let o = {
doSome() {
//...
},
doOther(param) {
// ...
}
};
不管函数或者方法是如何声明的,要看这个函数或者方法最终是谁调用的
谁最终调用这个函数或方法,那么这个函数或方法中的this就是谁.
全局函数相当于给window对象添加了一个方法,所以调用welcome()和window.welcome()本质是一样的,所以welcome函数最终是window对象在调用,所以welcome函数内部的this就指向了window对象.运行结果如下:
var userName = 'zhang';
function welcome(){
console.log(this.userName);
console.log(this);
}
welcome();
window.welcome();
sayHi方法是由obj对象点出来调用的,所以sayHi方法中的this指向obj对象.所以输出的是obj对象中的name的值, 结果如下:
var name = 'zhang';
var obj = {
name : 'shuai',
sayHi: function(){
console.log(this)
}
}
obj.sayHi()
构造函数中的this
直接调用构造函数(window)
function Student(name,age){
this.name = name;
this.age = age;
}
var s1 = Student('zhang',20);
console.log(s1);
console.log(window.name);
调用Student函数的时候没有用new关键字,那么这就意味着Student函数是window点出来调用的,所以Student函数中的this就是window.然后函数内部没有写return关键字那默认返回值是undefined
使用new调用构造函数(对象)
function Student(name,age){
this.name = name;
this.age = age;
}
var s1 = new Student('zhang',20);
console.log(s1);
大家都知道new关键字会创建一个对象,并且会把构造函数中的this指向这个对象,并且还会把这个对象自动返回, s1就是那个new关键字创建并返回的对象
为构造函数手动return后的情况
function Student(name,age){
this.name = name;
this.age = age;
console.log(this);
return {name:'shuai',age:18};
}
var s1 = new Student('zhang',20);
console.log(s1);
因为new关键字会自动帮我们返回他创建的对象,所以一般在构造函数中是不用写return返回值的, 那万一构造函数中就是写了return返回值会怎么样呢?
这个时候就要看return关键字后面返回的值是什么类型的,如果返回的是数值类型/字符串类型/布尔类型/null/undefined,那么会忽略这个返回值. 如果返回的值不是这些类型的,比如是一个数组或者一个对象,那么这个返回值会覆盖原来new关键字自动返回的那个对象. 所以上面代码运行的结果如下:
但是构造函数中的this还是new关键字创建的那个对象, 只是这个对象返回的时候被return关键字返回的新对象给覆盖了,所以s1接受不到而已.
var userName = 'zhang';
function welcome(){
console.log(this.userName);
console.log(this);
}
welcome();
上述代码中welcome函数中this指向window对象, 输出的是window, 但是我们现在想让welcome函数中的this指向obj对象,那应该怎么做呢?
let obj = {
name: 'zhang',
age: 18
};
function welcome(){
console.log(this);
};
welcome.call(obj);
这个时候,要实现这个效果,要想让welcome函数中的this指向obj, 就要使用函数上下文调用模式,比如call、 apply、 bind。
fun.bind(thisArg)
var name = 'ddd'
var a = {
name:'luoshushu',
fn:function(){
console.log(this.name)
}
}
var obj = {
name:'李四'
}
a.fn() //luoshushu。 this指向a对象
a.fn.bind(window)() //ddd。this指向window
a.fn.bind(obj)() //李四。this指向obj
默认指向按钮button的,bind()改变了他的this指向
document.querySelector('button').addEventListener('click', function() {
console.log(this);
}.bind({
name: "张珊",
age: 18
}));
fun.call(thisArg,arg1,age2,...)
注意:
function a(){
console.log(this) //输出函数a中的this对象
}
function b(){} //定义函数
var obj = {name:'luoshushu'}; //定义对象
a.call() // Window
a.call(null) // Window
a.call(undefined) // Window
a.call(1) // Number
a.call(' ') // String
a.call(true) // Boolean
a.call(b) // function b(){}
a.call(obj) // Object。{name: "luoshushu"}
//借用数组方法。(forEach)
function sum(){
var result = 0;
// 将数组中的forEach应用到arguments上
Array.prototype.forEach.call(arguments,function(value){
console.log(value) // 遍历出2,5,7,8,2,6
result += value
})
console.log(result) //30
}
sum(2,5,7,8,2,6)
//对象转换成数组
function sum2(){
//slice返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。且原始数组不会被修改。
var args = Array.prototype.slice.call(arguments,0)
console.log(Array.isArray(args)) //true
console.log(args) //[23, 234, 6, 5]
}
sum2(23,234,6,5)
//获取数组中的最大值和最小值
var arr = [2,34,66,7,15,-123]
var maxArr = Math.max.apply(null,arr) //66
var maxArr1 = Math.max.call(null,2,34,66,7,15,-123) //66
fun.apply(thisArg,[argsArray])
function a(x,y,z){
console.log(x,y,z)
}
a.apply(null,[1,2,3]) // 1 2 3
apply、call、bind三者区别:
var obj = {
a: 99
}
var foo = {
getX : function(){
return this.a
}
}
foo.getX.bind(obj)() // 99
foo.getX.call(obj) // 99
foo.getX.apply(obj) // 99
相同点:
不同点:
访问器属性不包含数据值。它包含一对getter和setter函数。当读取访问器属性时,会调用getter函数并返回有效值;当写入访问器属性时,会调用setter函数并传入新值,setter函数负责处理数据。
// 对象成员:属性,方法
// 属性:类似于变量
// 方法:类似于函数
const product = {
data: [
{ id: 1,name: '电脑',num: 5,price: 199},
{ id: 2,name: '电视机',num: 88,price: 2399},
{ id: 3,name: '洗衣机',num: 20,price: 599}
],
// 计算总金额方法
// ES6的方法的简化,将冒号和function关键字删除 getAmounts: function()
getAmounts(){
return this.data.reduce((t,c)=>(t += c.price*c.num),0);
},
// 访问器属性:将一个方法伪装/包装成一个属性
// get 读取 set 写操作
get total(){
return this.data.reduce((t,c)=>(t += c.price*c.num),0);
},
// 修改洗衣机的价格
set setPrice(price){
this.data[2].price = price;
console.log(this.data);
}
};
// console.log(`总金额:${product.getAmounts()}元`) // 总金额:224087元
// 不想用方法,想以属性的方式来获取总金额
console.log(`总金额:${product.total}元`);
// 访问器属性
product.setPrice = 99999;
一.访问器属性定义:
访问器属性由get和set方法定义。
(1).读取属性的时候调用get方法。
(2).设置属性的时候调用set方法。
let web={
_webName:Hello World!,
get webName(){
return this._webName;
},
set webName(webName){
this._webName=webName;
}
}
访问器属性的两个方法都是可选的,省略get,那么无法读取该属性,省略set,那么此属性只读。
上面是在字面量中定义访问器属性,当然也可以利用Object.defineProperty方法定义。
let web={
_webName: "zhang"
}
Object.defineProperty(web, 'webName', {
get: function() {
return this._webName;
},
set: function(webName) {
this._webName=webName;
}
});
二.访问器属性优先级:
访问器属性的优先级高于同名的普通属性
let user = {
data: {name},
get name(){
return this.data.name;
},
set name(v){
this.data.name = v;
}
};
// 调用的set方法
user.name = 'zhang';
// 调用的get方法
console.log(user.name);
通过一些特殊结构可以让js代码加载时,要么跳过一部分不加载,要么循环加载一部分代码,它包含条件分支语句和循环语句。
条件分支语句
单分支if语句
if (/* condition */) {
// code
} else {
// code
}
判断成绩是否及格
let score = 59;
if(score>=60){
console.log("及格");
}
多分支if语句
if (/* condition1 */) {
// code
} else if (/* condition2 */) {
// code
} else {
// code
}
判断成绩是否及格
let score = 59;
if(score>=60){
console.log("及格");
}else {
console.log("不及格")
}
三元表达式
语法:布尔表达式 ? 真时的值 : 假时的值
var a;
// 如果a不为空或undefined,那么就取a本身的值,否则赋予1作为默认值。
a = a ? a : 1;
判断成绩是否及格
let score = 59;
console.log(score>=60?"及格":"不及格");
switch语句
switch (statement) {
case value1:
// code
break;
case value2:
// code
break;
...
default:
// code
}
默认会根据statement的结果,与case后的value进行匹配,满足匹配条件则执行里面的代码,否则跳过该case;break用来打断后续的匹配;default可以不写。
let score = 59;
switch (true){
case score>=60 && score<80:
console.log("合格");
break;
case score>=80 && score<=100:
console.log("学霸");
break;
case score>100 || score<0:
console.log("非法数据");
break;
default:
console.log("补考");
break;
}
let status = "success";
switch(status.toLowerCase()){
case "success":
console.log("请求成功!");
break;
case "error":
console.log("请求失败!");
break;
default:
console.log("非法请求!")
}
for循环
/*
for (index_init; condition; step) {
// code
}
*/
for(let i = 0; i < 10; i++) {
console.log(i);
}
do while循环
do {
// code
} while(condition);
while循环
while(condition) {
// code
}
break语句
用来打断当前的循环,跳出该层循环
continue语句
用来中断执行后续该次循环的语句,通俗的说就是跳过continue后面的语句,马上开始下一次循环。
使用for+if对集合进行穷举和匹配
for (let i = 0; i < 1000; i++) {
if (i % 3 === 2) {
console.log(i);
}
}
穷举0~999之间满足i%3=2的所有情况
累加1~10的和
let total = 0;
for (let i = 1; i <= 10; i++) {
total += i;
}
console.log(total);
作业:
// 数组解构
[a, ...b] = [1, 2, 3,4];
// 对象解构
({name,age}={name:"zhang",age:18})
// 传参解构
setUser = function (id, { name, email, status }) {
return { id, name, email, status };
};
let user = setUser(10, {
name: "zhang",
email: "zhang@qq.com",
status: true,
});
let user = {
data:[{name:"张三",age: 18,gender:"男"},
{name:"李四",age: 15,gender:"男"},
{name:"王五",age: 23,gender:"女"},
{name:"赵六",age: 26,gender:"男"}],
get info(){
console.log(this.data);
},
set userAdd(v){
this.data.push(v);
console.log(this.data);
}
}
user.info;
user.userAdd = {name:"七七",age: 16,gender:"女"};