1. 对象的基本使用
- 对象只是一种特殊的数据。对象拥有属性和方法;
- 对象内部属性由key: value的键值对组成, 其中key是字符串类型(大部分情况下引号会被省略,另外es6后可以用Symbol类型), vaule可以是任意类型, 包括基本数据类型, 对象, 函数等;
- 点符号要求key是有效的变量标识符,不能包含空格, 不能以数字开头, 也不能包含某些特殊字符, 这个时候我们可以使用[]
// 1. 对象的基本结构 var person = { name: "zgc", age: 18, eat: function () { console.log("eating"); }, // 这种情况下需要引号 "my friend": "wf", }; console.log(person); // 2. 对象的常见操作 // (1) 对象的创建方式: // 方式1:字面量创建 var obj1 = { name: "zgc", age: 10, running: function () { console.log("running"); }, }; // 方式2: new Object var obj2 = new Object(); // 方式3: new 其他类 function Person() {} var obj3 = new Person(); // (2) 对象的增删改查 // 查询对象属性 console.log(obj1.age, obj1.name); // 10 'zgc' obj1.running(); // running // 修改对象属性 person.age = 40; console.log(person.age); // 40 // 添加对象属性 obj2.name = "zgc"; obj2.study = function () { console.log("study"); }; console.log(obj2); obj2.study(); // study // 删除对象属性 // delete操作符 delete person.name; console.log(person); // 3. 方括号的使用 // 点符号要求key是有效的变量标识符,不能包含空格, 不能以数字开头, 也不能包含某些特殊字符, 这个时候我们可以使用[] var message = "Hello World"; var info = { name: "zgc", age: 29, "my friend": "wf", "paly games": function () { console.log("games"); }, [message]: "你好, 世界", }; console.log(info.name === info["name"]); //true console.log(info["my friend"]); // wf info["paly games"](); // games var play = "paly games"; info[play](); // games console.log(info[message]); // 你好, 世界
2. 对象的遍历
var info = { name: "zgc", age: 18, height: 1.88, }; // Object.keys(): 拿到对象的所有属性名, 返回一个由属性名组成的数组 // 1. for const arr = Object.keys(info); console.log(arr); // ['name', 'age', 'height'] for (var i = 0; i < arr.length; i++) { var value = info[arr[i]]; console.log(value); // zgc 18 1.88 } console.log("---------------------------"); // 2. for in for (var key in info) { console.log(key); console.log(info[key]); } // name; // zgc; // age; // 18; // height; // 1.88; // 3. for of: 默认情况下是不能遍历对象的 for (var key of info) { // 报错: info is not iterable }
3. 内存
- 程序是需要加载到内存中来执行的, 我们可以将内存划分为两个区域: 栈内存和堆内存;
- 原始类型占据的空间是在 栈内存(stack) 中分配的
- 对象类型占据的空间是在 堆内存(heap) 中分配的
- 值(基础)类型:直接存储在栈中的数据,其操作方式为传值;
- 引用类型:存储在栈中的是该对象的引用,真实的数据存放在堆内存里,其操作方式为传址;
// 1. var obj = { name: "zgc", age: 18, }; var info = obj; obj.age = 20; console.log(obj.age, info.age); // 20 20 var msg = "Hello"; var detail = msg; console.log(msg, detail); // Hello Hello msg = "World"; console.log(msg, detail); // World Hello var obj2 = {}; var obj3 = {}; // 2. console.log(info === obj); // true; console.log(obj2 === obj3); // false; var msg1 = "Hello"; console.log(msg === msg1); //true var user = { name: "zgc", friend: { name: "wf", }, }; var friend = user.friend; friend.name = "wlc"; console.log(user.friend.name); // wlc; // 3. function foo(a) { a = 200; } var baz = 100; foo(baz); console.log(baz); // 100 // 4. function foo1(a) { a.age = 18; // 创建了一个新对象, 没有对传入对象进行修改 a = { name: "wf", }; } var baz = { name: "zgc", }; foo1(baz); console.log(baz); // {name: 'zgc', age =18}
4. 工厂函数
对象都是通过工厂造就的全新的对象。
优势:
- 工厂函数可以解决创建多个类似功能对象的问题。
缺点:
- 工厂模式无法解决对象的识别问题: 不能明确表示对象是什么类型的, 都是Object类型
function createPerson(name, age, height) { let o = new Object(); o.name = name; o.age = age; o.height = height; o.sayName = function () { console.log(this.name); }; return o; } function createStudent() { let o = {}; return o; } let person1 = createPerson("zgc", 23, "1.88"); let person2 = createPerson("wf", 25, "1,89"); let student = createStudent(); console.log(person1, person2, student); // 在打印对象时, 都是Object类型的对象: Object Object Object
5. 构造函数
工厂函数, JS已经默认提供了一种更加符合JavaScript思维方式(面向对象的思维方式)的创建对象的规则
- 构造函数也称之为构造器, 通常是我们在创建对象时会调用的函数
- 构造函数的函数名通常使用大写字母开头
如果一个函数被new操作符调用 :
- 首先在堆内存中创建了一个新的空对象
- 设置原型,将对象的原型设置为构造函数的prototype属性。
- 让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
- 判断函数的返回值类型,如果是值类型,返回创建的对象(注: 不写rteturn默认返回undefined, 是值类型)。如果是引用类型,就返回这个引用类型的对象。
function Person(name, age, height) { this.name = name; this.age = age; this.height = height; this.sayName = function () { console.log(this.name); }; } function Student() {} var person1 = new Person("zgc", 23, "1.88"); var person2 = new Person("wf", 25, "1,89"); var student = new Student(); console.log("构造函数", person1, person2, student); // 在打印对象时, 可以看到对象的具体类型: Person Person Student
6. 对象额外补充
// 1. 全局对象: 浏览器中存在一个全局对象window console.log(window); // 作用1: 查找变量时, 最终会找到window身上, 如果window也不存在该变量就会报错 // 作用2: 将一些浏览器全局提供给我们的变量/方法/对象放在window上面, 方便我们使用 // 作用3: 使用var定义的全局变量会被添加到window上面 var message = "zgc"; console.log(window.message); // zgc function foo() { // abc(); // abc is not defined alert("zgc"); // alert方法在window上, 所以能够使用 console.log(window.console === console); // true } // foo(); // 2. 函数也是一个对象 var foo = function () {}; // 存储在堆内存中 function bar() {} // 存储在堆内存中 var baz = foo; // 存储在堆内存中, baz和foo都是同一个内存地址, 指向同一块存储数据
7. 原始类型的包装类型
JavaScript的原始类型并非对象类型, 所以从理论上来讲, 它们是没有办法获取属性和调用方法的; 但是在实际上,每当读取一个基本类型的时候,js内部会自动创建一个基本包装类型对象,可以让我们调用一些方法来操作; 该包装类型只存在于一行代码的执行瞬间,然后立即销毁
JavaScript 你不知道的基本包装类型 - percy507的编程之路 - SegmentFault 思否
- js中为了便于基本类型操作,提供了5个特殊的引用类型:Boolean、Number、String、Symbol、BigInt它们具有基本类型特殊行为。
- 即除了 null 和 undefined,所有的原始值都有等价的、由对象包装原始值的形式表达,null 和 undefined没有任何方法. 也没有对应的对象包装类型
- 引用类型与基本包装类型的主要区别就是对象的生存期
使用 new 操作符创建的引用类型的实例,在执行流离开当前作用域之前,会一直保存在堆内存中。而后台自动创建的基本包装类型的对象,则只存在一行代码的执行瞬间,然后立即被销毁。这意味着我们不能为基本类型的值添加属性和方法。
// JavaScript的原始类型并非对象类型, 所以从理论上来讲, 它们是没有办法获取属性和调用方法的 // 但是, 在开发中我们经常会看到类似操作: var message = "Hello World"; console.log(message.length); // 11 var num = 12.34567; console.log(num.toFixed(2)); // 12.35 // 为什么会出现这种现象呢? // 原始类型是简单的值, 默认不能调用属性和方法 // 这是因为JS为了使其获取属性和调用方法, 对其封装了对应的包装类型 // 案例: var str = "hello zgc"; var str1 = str.substring(2); // 等价于: var str = new String("hello zgc"); // 创建String类型的实例, 浏览器内部默认执行 var str1 = str.substring(2); //在实例上调用指定的属性或方法 str1 = null; // 销毁这个实例 // 不能为基本类型的值添加属性和方法 var str = 'zgc' str.age = 23 console.log(str.age) // undefined
8. 包装类型-Number类型
// Number实例方法: var num = 3.1415926; // 等价于new Number(3.1415926) -> 包装类型 // 1. toString(base), 将数字转化为字符串, 并且按照base进制进行转化 // base的范围是 2 - 36, 默认情况下是10 var str = num.toString(); console.log("str", str, typeof str, "num", num, typeof num); // str 3.1415926 string num 3.1415926 number console.log(num.toString(2)); // 转化为2进制的字符串类型 console.log(num.toString(8)); // 转化为8进制的字符串类型 console.log(num.toString(16)); // 转化为16进制的字符串类型 // 2. toFixed(digits) 格式化一个数字, 保留digits小数位, 会进行四舍五入 // digits的范围是 0 - 20(包含) 之间 var pi = num.toFixed(3); // 返回值是string类型 console.log(pi); // 3.142 // Number类方法: var msg = "123.321"; console.log(Number(msg)); // 123.321 // 1. Number.parseInt(string, radix), 将字符串解析成整数, 也有对应的全局方法parseInt // radix(可选 ):表示要解析的数字的基数。该值介于 2 ~ 36 之间。 // 值不会进行四舍五入 console.log(Number.parseInt(msg)); // 123 // 在window上的方法 console.log(parseInt(msg)); // 123 // 2. Number.parseFloat(), 将字符串解析成浮点数, 也有对应的全局方法parseFloat console.log(Number.parseFloat(msg)); // 123.321 console.log(parseFloat(msg)); // 123.321 console.log(window.parseInt === Number.parseInt); //true
9. 内置对象-Math
- Math是一个内置对象(不是构造函数), 它拥有一些
数学常数属性和数学函数方法
// Math是一个内置对象(不是构造函数), 它拥有一些数学常数属性和数学函数方法 console.log(typeof Number); // function console.log(typeof Math); // object // 1. pi的值 console.log(Math.PI); // 3.141592653589793 // 2. 向上舍入取整 console.log(Math.ceil(3.55)); // 4 // 3. 向下舍入取整 console.log(Math.floor(3.55)); // 3 // 4. 四舍五入取整 console.log(Math.round(3.55)); // 4 // 5. 生成 [0, 1)的随机数 console.log(Math.random()); // 6. Math.pow(x, y) x的y次幂 console.log(Math.pow(2, 3)); // 8 // 7. 取绝对值 console.log(Math.abs(-32)); // 32 // 案例1: 生成 [5, 50) 的随机整数 var num = Math.floor(Math.random() * 45) + 5; console.log(num);
10. 包装类型-String类型
基本使用
var msg = "Hello World"; // 1. 获取字符串长度 console.log(msg.length); // 11 // 2. 返回在指定位置的字符 console.log(msg[4]); // o console.log(msg.charAt(0)); // H // 这两种方法的区别是, 访问不到字符位置时charAt返回一个空字符串, 索引方式返回undefined // 3. 字符串遍历 // for普通遍历 for (var i = 0; i < msg.length; i++) { console.log(msg[i]); } // for...of // 对象默认不支持for...of // String对象内部是将字符串变成了一个可迭代对象, 所以能够使用for...of for (var char of msg) { console.log(char); }
修改字符串
- 字符串在定义后是不可以修改的, 所以下面操作是没有意义的
- 但是可以给变量重新赋值新的字符串
msg[2] = "q"; console.log(msg); // my name is zgc // msg中所有字符大写 toUpperCase // msg中所有字符小写 toLowerCase var msg1 = msg.toUpperCase(); // 根据原来字符串生成新的字符串, 原字符串没有改变 console.log(msg1, msg); // MY NAME IS ZGC my name is zgc // 替换字符串 var msg2 = msg.replace("zgc", "wf"); // 根据原来字符串生成新的字符串, 原字符串没有改变 console.log(msg2, msg); // my name is wf my name is zgc
查找字符串
- 在一个字符串中查找或者获取另一个字符串
// (1) indexOf(): 对大小写敏感! // 搜索到, 返回字符串所在的索引位置, 没有搜索到返回-1 var str = "Hello world!"; console.log(str.indexOf("Hello")); // 0 console.log(str.indexOf("World")); // -1 console.log(str.indexOf("world")); // 6 // (2) includes(): 返回布尔值,表示是否找到了参数字符串 let s = "Hello world!"; console.log(s.includes("Hello")); // true // 支持第二个参数,表示开始搜索的位置 console.log(s.includes("Hello", 6)); // false console.log(s.includes("world", 6)); // true
获取子字符串
- slice: 提取字符串的片断,并在新的字符串中返回被提取的部分, 原字符串不变
- slice(start,end); 取值范围: [start,end), 且允许负值
- start :要抽取的片断的起始下标。如果是负数,则该参数规定的是从字符串的尾部开始算起的位置。也就是说,-1 指字符串的最后一个字符,-2 指倒数第二个字符,以此类推。
- end:紧接着要抽取的片段的结尾的下标。若未指定此参数,则要提取的子串包括 start 到原字符串结尾的字符串。如果该参数是负数,那么它规定的是从字符串的尾部开始算起的位置。
var str = "abc def ghk"; var newStr1 = str.slice(6); console.log(str, newStr1); //abc def ghk f ghk var newStr2 = str.slice(2, 5); console.log(newStr2); // c d var newStr3 = str.slice(4, -1); console.log(newStr3); // def gh
其他常用方法补充
// 6. 字符串拼接 // 方式1: concat() var a = "abc"; var b = "def"; var c = "gh"; var d = a.concat(b).concat(c); var e = a.concat(b, c, "zgc"); console.log(d); //abcdefgh console.log(e); //abcdefghzgc // 方式2: + var f = a + b + c; console.log(f); // abcdefgh // 7. 删除首尾空格: str.trim() console.log(" zgc ".trim()); // 8. 字符串切割 split(): 把字符串分割为字符串数组 // 原字符串不变 var message = "abc-def-ghi-jkl"; var str = "abc def ghi jkl"; console.log(str.split(" ")); //["abc", "def", "ghi", "jkl"] console.log(message.split("-")); //["abc", "def", "ghi", "jkl"] console.log(str.split("")); //["a", "b", "c", " ", "d", "e", "f", " ", "g", "h", "i", " ", "j", "k", "l"] console.log(str.split(" ", 3)); //["abc", "def", "ghi"]