外观
面向对象
使用内置对象创建对象:
let str = new String('初始化String'); //创建字符串
let str1 = '初始化String';
let func = new Function(x, alert(x));
let o = new Object();创建对象的第二种方法:直接定义:
let person = {
name: '张三',
age: 18
};
document.getElementsByClassName('person').innerHTML = '现在' + person.name + person.age + '岁了' + '<br\>';高级对象创建方法:使用this关键字。
function person() { //构造方法
this.name = '李四';
this.age = 19;
}
let person2 = person(); //创建对象的方法使用prototype构造:
let person3 = {};
person3.prototype.name = '王五';
person3.prototype.age = 20; //通过原型prototype更改对象的属性this和prototype定义的不同是属性的占用空间不同,使用this关键字是在内存中开辟内存空间,使用原型是改变父级数据,因此原型比this节省空间。
创建对象的常用方法
- 工厂模式——不推荐
使用工厂模式需要注意以下几点:方法中定义对象,并定义对象的各种属性,虽然属性可以是方法,但是建议将属性为方法的属性定义到方法之外,这样可以避免重复创建该方法。
引用该对象时不要使用new创建对象,在方法的最后返回该对象,这样直接调用这个方法就可以接收到方式返回的对象。
function Parent1() {
let Child = new Object();
Child.name = '张三';
Child.age = 3;
return Child;
};
let obj1 = Parent1();- 自定义方法构造方式——不推荐
此方法同工厂模式一样,若属性为方法,建议将属性为方法的属性定义到方法之外。
function Parent2(name, age) {
this.name = name;
this.age = age;
};
let obj2 = new Parent2(name = "张三", age = 18);- 原型模式——不推荐
let age = function(){
return 18;
};
function Parent3(){
Parent3.prototype.name = '张三';
Parent3.prototype.age = age();
};
let obj3 = new Parent3();- 原型模式和构造方法模式——推荐
这种模式是将不是方法的属性定义在构造方法内,把是方法的属性放在方法外面,用原型定义。
function Parent4(){
this.name = '张三';
this.age = 18;
};
Parent4.prototype.lev = function(){
return this.name;
}
let obj4 =new Parent4();
document.write(obj4.lev()) //输出张三- 动态原型模式
function Parent(){
this.name = '张三';
this.age = 18;
if (typeof Parent._lev == undefined) {
Parent.prototype.lev = function(){ //在该模式中,属性为方法的属性在方法定义,但是需要保证创建该对象的实例时,属性的方法不会重复创建
return this.name;
}
Parent._lev = true;
}
}有关原型的知识请向下查阅。
对象访问语句
首先先定义如下变量:
let obj = {
name: '张三',
age: 18,
sex: '男'
};- for in语句 它用来遍历对象的每一个属性
for (letiable in obj) {
document.write('obj的' + letiable + '属性值为' + obj[letiable] + '<br\>');
}- with语句
语法是 with (object) {statements} 其中obj代表对象名 使用with语句的作用是不需要重复指定对象名称,如下面的代码:
//不使用with
document.write('不使用with:obj的name属性=' + obj.name + ' obj的age属性' + obj.age);
//使用with
with (obj) {
document.write('使用with:obj的name属性=' + name + ' obj的age属性' + age);
}对象序列化
对象序列化的意义是将对象的属性和值转换为字符串,序列化是对象转换为JSON,使用JSON.stringify(),反序列化是将JSON转换为对象,使用JSON.parse()。
对象序列化使用的方法是String.stringify(value[,replacer[,space]])。[]表示可选参数,
value是有效的JSON字符串。
replacer:用于转换结果的对象和数组。若replace为方法,则JSON.stringify()将调用此方法,并传入每个成员的键和值。使用返回值而不是原始值。若此方法返回undefined则排除成员。跟对象的键是一个空字符串:''。若replacer是一个数组,则仅转换数组中具有键值的成员。成员的转换顺序与每个键在数组的顺序一样。当value参数也为数组时,将忽略replacer数组。
space:文本添加缩进、空格和换行符,若replacer是一个数字,则返回值文本在每个级别缩进指定数目的空格,若space大于10,则文本缩进指定数目的空格,若space大于10,则文本缩进10个空格。space可以使用非数字,如\t。
返回值:返回包含JSON文本的字符串
let obj = {
name: '张三',
age: 18
};
//实例1:只有一个参数情况
document.write('只有一个参数情况' + '<br\>');
document.write(JSON.stringify(obj) + '<br\>');
//实例2,有多个参数
document.write('有多个参数的情况' + '<br\>');
document.write(JSON.stringify(obj, null, 4) + '<br\>'); //4相当于一个制表符(暂时无效果)
//对象反序列化:是通过JSON.parse()实现的
//JSON.parse(text, [receiver]) 其中text是一个有效的JSON字符串,receiver是结果转换方法,将为此对象的每个成员调用此方法
let text = '{"name":"李四","age":19}';
document.write('解析JSON:' + '<br\>');
let result = JSON.parse(text);
for (letiable in result) {
document.write('结果的' + letiable + '属性 = ' + result[letiable] + '<br\>');
}运行结果:
只有一个参数情况
{"name":"张三","age":18}
有多个参数的情况
{ "name": "张三", "age": 18 }
解析JSON:
结果的name属性 = 李四
结果的age属性 = 19创建对象的过程
这里的创建对象是指使用构造函数和new实例化新对象的过程。
function Obj(val=null) {
this.val = val
this.print = function () {
console.log(this.val)
}
}
let obj = new Obj('新对象');
obj.print()- 实例化执行过程:创建新对象、构造函数this指向新对象、执行构造函数代码、修改this,指向新的属性、返回新对象。
对象的拷贝
对象的拷贝不能只用一个简单的赋值语句实现。
const obj1 = {key: "val1"};
const obj2 = obj1;
obj2.key = "val2"; //在这里obj1的key也会变 因为它们两个指向的内存空间是一致的若对象里的属性类型是简单数据类型,则可以使用Object.assgin方法实现浅拷贝,但不能实现深层拷贝。
const obj1 = {key: "val1"};
const obj2 = {};
Object.assgin(obj2, obj1)
obj2.key = "val2"; //在这里obj1的key也会变 因为它们两个指向的内存空间是一致的const obj1 = {obj: {key: "val"}};
const obj2 = {};
Object.assign(obj2, obj1);
obj2.obj.key = "val2"; //在这一步obj1.obj.key也会更改 因为只拷贝了浅层的属性,obj仍然指向同一个对象空间深拷贝有三个方法:递归、lodash/cloneDeep、JSON.stringify。
递归实现深拷贝的实现方法是遍历对象的键,若键是普通值则直接将值赋值给新对象的属性即可,否则递归深拷贝。
let oldObj = {
number: 6,
boolean: true,
str: "你好",
obj: {
key1: "value1",
key2: "value2"
},
arr: [1, 2, 3, 4]
}
let newObj = copyDeep(oldObj);
function copyDeep(obj) {
// 检查obj是否为null,因为typeof null也会返回'object'
if (obj === null) return null;
// 处理数组
if (Array.isArray(obj)) {
return obj.map(item => copyDeep(item));
}
// 处理对象
if (typeof obj === 'object') {
let newObj = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) { // 确保key是obj自身的属性
newObj[key] = copyDeep(obj[key]);
}
}
return newObj;
}
// 处理基本类型和函数
return obj;
}第二种方法使用了一个第三方库loadsh,这里不演示。
第三种方法最为简便,一行代码就能搞定。
let newObj = JSON.parse(JSON.stringify(oldObj))其他知识
- 运算符
其他运算符和其他编程语言的运算符相类似,这里不再赘述,演示一下其他运算符。
void运算符:用于计算括号内的值,并返回undefined,语法是void()。
let let1 = void (1 + 1);
document.write('let1 = ' + let1 + '<br\>');- typeof运算符:计算表达式的结果类型,值只能是number string boolean object function undefined中的一个。
let let2 = '123';
let let3 = null;
let let4 = 1.2;
document.write('let2:' + typeof let4 + ' let3:' + typeof let3 + 'let4:' + typeof let4 + '<br\>');