【面向对象】| 青训营

56 阅读7分钟

一、认识对象

对象(object)是 “键值对” 的集合,表示属性和值的映射关系。

()代表优先级,{}对象[]数组

var xiaoming = {
    name: '小明',
    age: 12,
    sex: '男',
    hobbies: ['足球', '编程']
};
  • 属性名(键名,key): 属性值(value)
  • JS 中,大括号表示对象
  • 最后的属性后面不加逗号
  • {} 后加上分号

1.1 对象的语法

k 和 v 之间用冒号分隔,每组 k:v 之间用逗号分隔,最后一个 k:v 对后可以不书写逗号。

var obj = {
    k: v,
    K: v,
    K: v,
    K: v
};

1.2 属性是否加引号

如果对象的属性键名不符合 JS 标识符命名规范,则这个键名必须用引号包裹。

注意:对象中的 key 本身就是字符串格式,只是符合 JS 标识符命名规范的可以省略引号!

var xiaoming = { 
    name: '小明',
    age: 12,
    sex: '男',
    hobbys: ['足球', '游泳', '编程'],
    'favorite-book': '舒克和贝塔'
    // 属性名中有短横,不符合JS标识符命名规范,属性名必须用引号包裹。
};

1.3 属性的访问

可以用“点语法”访问对象中指定键的值。

xiaoming.name;      // '小明'
xiaoming.age;       // 12
xiaoming.hobbys;    // ['足球', '游泳', '编程'] 

如果属性名不符合 JS 标识符命名规范,则必须用方括号的写法来访问。

方括号 [] 中只能是字符串类型!

任何对象的属性名都可以通过 [] 来访问,只要把属性名写为字符串的形式。

xiaoming['favorite-book'];  // '舒克和贝塔'

如果属性名以变量形式存储,则必须使用方括号形式。

var obj = {
    a: 1,
    b: 2,
    c: 3
};
​
var key = 'b';
console.log(obj.key);   // undefined
console.log(obj[key]);  // 2

1.4 属性的更改

直接使用赋值运算符重新对某属性赋值即可更改属性。

var obj = {
    a: 10
};
obj.a = 30;
obj.a++;

1.5 属性的创建

如果对象本身没有某个属性值,则用点语法赋值时,这个属性会被创建出来。

var obj = {
    a: 10
};
obj.b = 40;
var obj = {
    a: 10
};
obj['zjr-b'] = 40;

1.6 属性的删除

如果要删除某个对象的属性,需要使用 delete 操作符。

var obj = {
    a: 1,
    'zjr-b': 2
};
delete obj.a;
delete obj['zjr-b'];

二、对象的方法

2.1 认识方法

如果某个属性值是函数,则它也被称为对象的 “方法”。

var xiaoming = {
    name: '小明',
    age: 12,
    sex: '男',
    hobbys: ['足球', '游泳', '编程'],
    'favorite-book': '舒克和贝塔',
    // sayHello方法
    sayHello: function () {
        console.log('你好我是小明,今年12岁,我是个男生');
    }
};

2.2 方法的调用

使用 “点语法” 可以调用对象的方法。

xiaoming.sayHello();

2.3 方法和函数

方法也是函数,只不过方法是对象的 “函数属性”,它需要用对象打点调用。

在正式学习了什么是 “方法” 之后,就能深入理解之前我们学习的一些函数的书写形式了,比如:

console.log();
Math.ceil();

2.4 对象的遍历

和遍历数组类似,对象也可以被遍历,遍历对象需要使用 for...in... 循环。

使用 for...in... 循环可以遍历对象的每个键。

在后续的 ES6 相关课程中,还会学习新的对象遍历的方式。

【for...in...循环】

// k: 循环变量,它会依次成为对象的每一个键
// obj: 要遍历的对象
for (var k in obj) {
    console.log('属性' + k + '的值是' + obj[k]);
}

【案例】

var obj = {
    a: 11,
    b: 22,
    c: 88
};
for (var k in obj) {
    console.log('对象obj的属性' + k + '的值是' + obj[k]);
}
​
/*
对象obj的属性a的值是11
对象obj的属性b的值是22
对象obj的属性c的值是88
*/
var zjr = {
    name: 'jerry',
    love: [180, 18, 1800000],
    home: {
        mm: 'glp',
        bb: 'zyj'
    }
};
​
for (var i in zjr) {
    //i得到的是的是字符串形式的属性名
    console.log(i);
    console.log(typeof i);
    //获取属性名对应的值
    console.log(zji[i])
}
​
/*
name
string
string
love
string
object
home
string
object
*/
let students=[    {        name:'小明',age:18,gender:'男',hometown:'河北省'    },     {        name:'小红',age:19,gender:'女',hometown:'广东省'    },     {        name:'小蓝',age:20,gender:'男',hometown:'河南省'    }]
for(let i=0;i<students.length;i++){
    cosole.log(i)  //1 2 3 下标索引号
     cosole.log(students[i])  //{} {} {} 每一个对象
     cosole.log(students[i].name)  //小明 小红 小蓝 
}
//渲染例子
for(let i=0;i<students.length;i++){
    document.write(`
    <tr>
         <td>${i+1}</td>
         <td>${students[i].name}</td>
         <td>${students[i].age}</td>
         <td>${students[i].gender}</td>
         <td>${students[i].hometwon}</td>
    </tr>
    `)
}

for...in... 循环中,每一个迭代值是对应值的字符串形式。

2.5 内置对象

内置对象就是可以直接使用的对象,不需要使用字面量,Object以及构造函数创建的对象,js已经定义好的对象。

1、使用MDN(web开发文档网站)查询资料。

2、Math对象:是一个内置对象,主要用于数学运算的,不是一个函数对象(通过构造函数创建的对象叫函数对象),在创建Math对象时,不能使用new运算符(即不能new Math),

它的属性和方法在使用时采用:

Math.属性名

Math.方法名(参数)

(1)属性:

Math.PI:表示圆周率,一个圆的周长和直径之比,约等于 3.14159。

(2)方法:

a、Math.abs(x):返回参数x的绝对值

b、Math.floor(x):返回小于等于形参(给定参数)的最大整数,即一个数向下取整后的值。3.99,返回之后就是3。

c、Math.ceil(x):返回大于等于一个数的最小整数,即一个数向上取整后的值。3.99,返回之后的值就是4。

d、Math.max([x[,y[,...]]]):返回所有参数中的最大值。

e、Math.min([x[,y[,...]]]):返回所有参数中的最小值。

f、Math.pow(x,y):返回x的y次方。

g、Math.sqrt(x):返回x的算术平方根

h、Math.round(x):取整,x四舍五入以后的整数。

i、Math.random():返回0.0到1.0之间的随机数,包括0,不包括1。

j、Math.trunc():去除小数点后的所以有内容,没有四舍五入。

例子:

// 定义一个函数,用于生成min-max之间的随机数

//构造函数
​
    // function Random() {
​
    //     this.getRandom=function(min,max){
​
    //         let num = Math.random()*(max - min) + min;
​
    //         return Math.round(num);
​
    //     }
​
    // }
​
    // let t = new Random()
​
    // console.log(t.getRandom(10,100));
​
//普通函数
​
    // function Random(min,max) {
​
    //         let num = Math.round(Math.random()*(max - min) + min);
​
    //         return num;
​
    // }
​
    // let t = Random(10,100);
​
    // console.log(t);
​
    //定义一个3*4的二维数组,数组元素随机生成,将数组显示出来,并输出数组。
​
    //1、定义二维数组
​
    let arr = new Array(new Array(4),new Array(4),new Array(4));
​
    //2、随机生成二维数组中的元素,并显示
​
    let str ='';
​
    for (let i = 0; i < arr.length; i++) {//控制行
​
        for (let j = 0; j < arr[i].length; j++) {
​
//构造函数
​
            arr[i][j] =t.getRandom(10,100);
​
//普通函数
​
            // arr[i][j] =Random(10,100);
​
            str += arr[i][j]+'\t';
​
        }
​
        str += '\n';
​
    }
​
    console.log(str);

3、日期对象date

Date对象:是一个函数对象,,使用new运算符创建对象

(1)构造函数:

a、无参构造函数:new Date()使用的日期格式是:月日年

b、传入年月日、时分秒:new Date(年,月,日,时,分,秒),月份取值在0-11之间,0表示1月,11表示12月

c、传入字符串表示日期和时间:new Date('字符串')

d、传入一个整数:new Date(整数),整数代表毫秒数

(2)其他函数:

a、getFullYear():获取年份(年份四位)

b、getMonth():获取月份(0-11)

c、getDate():获取月份中的某一天(日期)(1-31)。

d、getDay():获取星期(0-6)0表示星期天。

e、getHours():获取小时数

f、getMinutes():获取分钟数

g、getSeconds():获取秒钟数;

h、getTime():获取1970年1月1日0时0分0秒到当前日期之间的毫秒数。

i、toLocaleDateString():方法返回该日期对象日期部分的字符串。(只有年月日)

j、toLocaleString(): 方法返回该日期对象的字符串。(年月日,时分秒都显示)

k、The toLocaleTimeString() :方法返回该日期对象时间部分的字符串(时分秒)

l、toTimeString() :方法以人类易读形式返回一个日期对象时间部分的字符串

三、String内置对象

1、String对象:字符串。用单引号或('')或双引号("")括起来的字符序列。

(1)创建方式:

a、字面量:' '或" "

b、使用构造函数:new String()

(2)字符串属性:length ------表示字符串的长度(字符串中字符的个数)

用法:字符串对象名.length

2、String对象的常用方法:字符串名.函数名(实参)

(1)charAt(index):返回字符串中,index位置上的字符。

返回下标为0的字符。index表示下标。

(2)charCodeAt(index)返回字符串中index位置上的字符的unicode编码。

ASCII码:是美国做的字符编码('a':97...)只支持英文字符,表示一个字符使用1个字节(byte),,即8个二进制位。

unicode码:国际标准化组织做的一套编码。表示一个字符使用2个字节, 支持中文。

ISO-8859-1码:不支持中文

GBK:支持中文

GB2312:支持简体中文

(3)concat(字符串):连接字符串,将两个及两个以上的字符串进行连接

concat可以连续拼接字符串。

(4)endsWith(子串):判断字符串是否以给定的子串结尾。

flag输出true或者false,s5输出剩余的字符。

(5)indexOf(子串):返回子串在字符串中首次出现的位置,返回下标

indexOf(子串,整数):返回从大于等于整数的位置开始查找子串第一次出现的位置。

(6)lastindexOf(子串):返回子串在字符串中最后出现的位置(下标)

(7)includes(子串):查找字符串中是否包含指定的子串。若包含返回true,不包含返回false。

(8)startsWith(子串):判断字符串是否以给定子串开头,是返回true,不是返回false。

(9)split(字符):将字符串分割成字符串数组。

(10)replace(oldstr,newstr):在字符串中查找oldstr第一次出现的位置,并用newstr替换它。

(11)stbstr(star,end):截取字符串中从start到end之间的字符串,不包含end。

(12)substtringr(star,end):截取start到end之间的子串,不包含end。

(13)trim():去掉字符串两端的空格。

(14)trimEnd():去掉字符串结尾的空白字符。

(15)trimStart():去掉字符串前面的空白字符。

(16)tolowerCase():将字符串中的所有字母转换为小写。

(17)toUPperCase():将字符串中的所有字母转换为大写字母。

(18)toString():将字符串对象转换成字符串,输出结果没有大括号括起,是一个普通的字符串。

(19)valueOf():将字符串对象转换成一个原始值。

四、字符串的不可变性:指当一个字符串被定义以后,它的内容是不变的。虽然通过调用相关函数看似改变了串的内容,实际是在内存中新开辟了一个空间存放新的串。

五、综合练习

image-20230323213034941

三、对象的深浅克隆

3.1 复习基本类型值和引用类型值

还记得我们之前学习过的基本类型值和引用类型值吗?

举例当 var a = b 变量传值时当用 == 比较时当用 === 比较时
基本类型值数字、字符串、布尔、undefined、null内存中产生新的副本比较值是否相等类型相等的前提下,比较值相等
引用类型值对象、数组等内存中不产生新的副本,而是让新变量指向同一个对象比较内存地址是否相同,即比较是否为同一对象比较内存地址是否相同,即比较是否为同一对象

对于引用类型的比较来说:== 与 === 是没有区别的!

var a = {};
var b = {};
var c = a;
​
console.log(a == b);    // false
console.log(a === b);   // false
console.log(a == c);    // true
console.log(a === c);   // true

3.2 对象是引用类型值

对象是引用类型值,这意味着:

不能用 var obj2 = obj1 这样的语法克隆一个对象。

使用 == 或者 === 进行对象的比较时,比较的是它们是否为内存中的同一个对象,而不是比较值是否相同。

【案例】

// 例子1
var obj1 = {
    a: 1,
    b: 2,
    c: 3
};
var obj2 = {
    a: 1,
    b: 2,
    c: 3
};
console.log(obj1 == obj2);      // false
console.log(obj1 === obj2);     // false
​
console.log({} == {});          // false
console.log({} === {});         // false
​
// 例子2
var obj3 = {
    a: 10
};
var obj4 = obj3;
obj3.a++;
console.log(obj4);              // {a: 11}
console.log(obj3 == obj4);      // true
console.log(obj3 === obj4);     // true

3.3 对象的浅克隆

复习什么是浅克隆:只克隆对象的 “表层”,如果对象的某些属性值又是引用类型值,则不进一步克隆它们,只是传递它们的引用。

使用 for...in... 循环即可实现对象的浅克隆。

【案例】

var obj1 = {
    a: 1,
    b: 2,
    c: [44, 55, 66]
};
​
// var obj2 = obj1;  这不是克隆,千万不能这样!!!!
​
// 实现浅克隆
var obj2 = {};
for (var k in obj1) {
    // 每遍历一个 k 属性,就给 obj2 也添加一个同名的 k 属性
    // 值和 obj1 的 k 属性值相同
    obj2[k] = obj1[k];
}
​
// 为什么叫浅克隆呢?比如 c 属性的值是引用类型值,那么本质上 obj1 和 obj2 的 c 属性是内存中的同一个数组,并没有被克隆分开。
obj1.c.push(77);
console.log(obj2);                  // obj2 的 c 属性这个数组也会被增加 77 数组
console.log(obj1.c == obj2.c);      // true,true 就证明了数组是同一个对象

3.4 对象的深克隆

复习什么是深克隆:克隆对象的全貌,不论对象的属性值是否又是引用类型值,都能将它们实现克隆。

和数组的深克隆类似,对象的深克隆需要使用递归。

面试时经常会考察深克隆算法,必须掌握。

【案例】

var obj1 = {
    a: 1,
    b: 2,
    c: [33, 44, {
        m: 55,
        n: 66,
        p: [77, 88]
    }]
};
​
// 深克隆
function deepClone(o) {
    // 要判断 o 是对象还是数组
    if (Array.isArray(o)) {
        // 数组
        var result = [];
        for (var i = 0; i < o.length; i++) {
            result.push(deepClone(o[i]));
        }
    } else if (typeof o == 'object') {
        // 对象
        var result = {};
        for (var k in o) {
            result[k] = deepClone(o[k]);
        }
    } else {
        // 基本类型值
        var result = o;
    }
    return result;
}
​
var obj2 = deepClone(obj1);
console.log(obj2);
​
console.log(obj1.c == obj2.c);      // false
​
obj1.c.push(99);
console.log(obj2);                  // obj2 不变的,因为没有“藕断丝连”的现象
​
obj1.c[2].p.push(999);
console.log(obj2);                  // obj2 不变的,因为没有“藕断丝连”的现象