1. js的特点以及解释性
JavaScript 是一种跨平台的脚本语言
1.1 解释型
JavaScript是一门解释型编程语言,解释型编程语言指代码不需要手动编译,而是通过解释器边编译边执行。所以要运行JS,我们必须现在计算机中安装
JS的解释器。像是我们使用的浏览器都已经自动集成了JS的解释器(我们也称它是JS引擎)。Node.JS也是JS引擎,它使得JS可以直接在计算机中运行。无论是浏览器还是Node.js都需要遵循ECMAScript(ES)标准。
1.1 函数式编程
在JavaScript中函数是一等公民,它可以像其他类型的值一样赋值给任意变量,也可以作为参数传递给其他函数。所以在JS中函数是非常非常重要,通过函数式编程可以编写出功能强大又灵活的代码。
1.2 单线程
JavaScript是一个单线程的编程语言。简言之,JS同一时间只能做一件事,一件事完成才会继续做另一件事。单线程降低了JS代码的复杂度,也在某些场景下使得JS性能变差,所以JS又为我们提供了异步的编程方式,以提高代码的运行速度。
1.3 面向对象
几乎所有的现代的编程语言都是面向对象的编程语言,JS也不例外。所谓的面向对象,指将一组相关的功能(数据)统一封装到一个对象中,使用功能时无需考虑其实现的细节,直接找到对应的对象即可完成功能的调用。
1.4 扩展ES
ECMAScript只是为我们定义最基本的语法,像是数据类型(原始值、对象)、运算符、流程控制语句等内容。为了使JS可以适用于不同的场景,在不同的JS解释器中还为我们提供了不同的扩展以增强其功能。像是浏览器中的DOM、BOM使得我们可以通过JS操作网页和浏览器。NodeJS中的fs模块可以使我们直接操作计算机系统中的各种文件。所以我们学习JS时,除了要学习ES标准以外,还要学习它的各种扩展,才能在不同的环境中发挥出JS的最大威力。
1.5 javascript书写顺序
<!-- 一般情况下 我们在body中书写的script代码都是放在最后一行的 -->
<script>
alert('拖鞋手牌拿好,二楼请')
</script>
<head>
<script>
alert('蔡徐坤是一个篮球运动员')
</script>
</head>
<body>
<script>
alert('鸡你太美')
</script>
</body>
<!-- 总结:js的代码是从上到下依次执行的 无论是在body还是head中 -->
总结:js文件放到哪里更好?
- 浏览器 对html页面内容的加载是顺序加载,也就是在html页面中前面先加载。当浏览器加载html文件并解析到
<head>
时,<body>
并没有被解析,浏览器会等到<head>
中的js部分执行完再加载页面。 - 如果把javascript放在head里的话,则先被解析,但这时候body还没有解析。(常规html结构都是head在前,body在后)如果head的js代码是需要传入一个参数(在body中调用该方法时,才会传入参数),并需调用该参数进行一系列的操作,那么这时候肯定就会报错,因为函数该参数未定undefined
- 从JavaScript对页面下载性能方向考虑:由于脚本会阻塞其他资源的下载(如图片等)和页面渲染,直到脚本全部下载并执行完成后,页面的渲染才会继续,因此推荐将所有的
<script>
标签尽可能放到<body>
标签的底部,以尽量减少对整个页面下载的影响。
1.6 js误区
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>10_js初体验-在哪个位置来书写script</title>
</head>
<!-- <script>
// 因为代码是从上到下依次执行的 所以在获取的时候 我们还没有看到d1呢 所以获取不到对象 更使用不了style
// 获取的div的对象
var div = document.querySelector('#d1')
div.style.color = 'green'
</script> -->
<body>
</body>
<div id="d1">苍茫的天涯是我的爱</div>
<script>
// 获取的div的对象
var div = document.querySelector('#d1')
div.style.color = 'purple'
</script>
<!-- 总结:在实际的企业级开发中 script标签都是放在body最后一行 -->
</html>
js因该写在body的最后一行,因为js是从上向下执行的,所以应该写在结构下面。不然获取不到元素
1.7 defer-延迟加载(了解)
<script src="./js/12_js初体验-延迟加载-defer.js" defer></script>
<script src="./js/12_js初体验-延迟加载2-defer.js"></script>
<script>
alert('中午吃盖饭')
</script>
总结: (1)在所有的js文件执行 才会执行延迟加载
(2)当有很多个defer文件的时候 我们依然是按照顺序来执行的
(3)defer只能应用在外部引入js的时候 才可以使用
1.8 异步加载JS文件-async
-
在默认情况下,网页都是同步加载外部 JavaScript文件的,在引入外部js文件时会阻塞dom的执行,为此在html4.01为script标签引入了async属性
-
现在可以为
<script>
标签设置 async属性,让浏览器异步加载 Javascript文件,即表示应该立即下载脚本,但不应妨碍页面汇总的其它操作。只对外部脚本文件有效。 -
因为是下载完立即执行,不能保证多个加载时的先后顺序,因此确保异步脚本之间互不依赖
<script src="js/13_js初体验-异步加载.js" async></script> //`async`
<script>
console.log(111)
// 比较耗时的操作 会耽误代码的执行 所以我们要把同步的任务改成异步
for(var i = 0; i < 1000; i++) {
for(var j = 0; j < 1000; j++) {
console.log(i,j);
}
}
console.log(222);
1.9 四种输出方式
1.9.1 alert 写入警告框
<script>
alert('这周变冷了')
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-67I1RrNE-1665326920509)(C:\Users\24601\AppData\Roaming\Typora\typora-user-images\image-20220920115544953.png)]
1.9.2 console.log() 写入浏览器控制台
<script>
// 控制台输出 控制台打印
// 我们一般在开发的时候都会利用这个方法来测试我们的代码
console.log('明星真优秀');
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jAY0PlA5-1665326920510)(C:\Users\24601\AppData\Roaming\Typora\typora-user-images\image-20220920115654568.png)]
1.9.3 document.write() 写入HTML输出
<a id="lian" href="#">点击</a>
let lian = document.querySelector('#lian')
lian.addEventListener("click",function(){
document.write('<h1>12456</h1>')
})
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ag1eBVaX-1665326920510)(C:\Users\24601\AppData\Roaming\Typora\typora-user-images\image-20220920115845457.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wjoknAJz-1665326920511)(C:\Users\24601\AppData\Roaming\Typora\typora-user-images\image-20220920115903639.png)]
会把原先的结构覆盖
####1.9.4 innerHTML 写入HTML元素
<a href="#" id="a1">
<em>尚硅谷</em>
前端学科
</a>
// innerHTML是获取标签中内容的
var a = document.querySelector('#a1')
console.log(a.innerHTML);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iswcWydm-1665326920511)(C:\Users\24601\AppData\Roaming\Typora\typora-user-images\image-20220920134411281.png)]
1.10 js语句格式
// 空格 推荐使用第二种书写方式
// 那么我们可以使用\ 来设置编写的时候 可以换行 但是并不是显示的时候可以换行
var a = 1;
var a = 1;
//实际的企业级开发 一行代码 不允许超过80个字符
console.log('故事里的小黄花,从出生那年就飘着,\ 童年的荡秋千,随记忆一直晃到现在...');
1.11 js严格模式
// 变量时可以重复定义的 但是不建议 我们在开发中也不会这样写
// var a = 1;
// var a = 2;
// var a = 3;
// console.log(a);
// 在默认情况下 js是非严格模式
// 不加var 也可以定义变量
// 但是在严格模式下 必须要加var 不加var就报错
// es6 都是严格模式
'use strict'
let b = 3;
console.log(b);
c = 4
console.log(c);
‘use strict’ 必须写在js代码第一行
1.12 变量提升
var a = 1;
console.log(a);
(1) 声明了一个a
var a;
console.log(a);
a = 1;
console.log(a);
(2) 如果变量没有
错误的内容是a is n
console.log(a);
(3) 因为默认情况
所以不会报错
a = 1;
console.log(a);
变量提升
// 将这个知识点的原因 是因为怕同学们瞎写
// js中带var的变量 会发生变量提升
console.log(a);
var a = 1;
console.log(a);
// 提升之后的代码
// 只是提升声明
var a;
console.log(a);
a = 1;
console.log(a);
1.13 变量交换
var a = 1;
var b = 2;
var temp = 0;
temp = a;
a = b;
b = temp;
console.log('a:'+a,'b:'+b);
2. javascipt数据类型
2.1 String
以单引号或者双引号包裹 (字符串类型)
longString = "This is a very long string which needs " +
"to wrap across multiple lines because " +
"otherwise my code is unreadable.";
2.1.1转字符串方法总结
1.String() 函数
2.toString() 方法
3.将数据 + ‘’ // 加空字符串
2.2 Number
在js中 无论是整数还是小数 那么统称为number类型 (数值类型)
在js中全部的字母都是大写的话 那么证明该变量是常量
// 在js中最大值 和最小值
// 如果你见到了全部的字母都是大写的话 那么证明该变量是常量
// Symbol 就是这个永恒不变
console.log(Number.MAX_VALUE);
console.log(Number.MIN_VALUE);
infinity
// 什么时候会发生正无穷 或者 负无穷
// 分母为0的时候 那么返回的结果是无穷
// 分子为正数 分母为0的时候 那么结果是Infinity
// var a = 1 / 0;
// console.log(a); // infinity
// var b = -1 / 0;
// console.log(b); // -infinity
计算时 只有 infinity == infinity 返回false
number-最大值最小值-正负无穷 (了解)
console.log(Number.MAX_VALUE); //最大值
console.log(Number.MIN_VALUE); //最小值
console.log(Number.POSITIVE_INFINITY); //正无穷
console.log(Number.NEGATIVE_INFINITY); //负无穷
2.2.1 number-浮点数溢出
// 浮点数溢出 一般应用于电商类的网站计算
var a = 0.1;
var b = 0.2;
var c = a + b;
console.log(c);
// 因为计算方式的问题 所以小数计算不够准确
// 但是整数的计算方式是正确的 所以我们需要
// 将小数扩大合适的倍数 然后再减小
var a1 = a * 10;
var b1 = b * 10;
var c1 = (a1 + b1) /10;
console.log(c1);
2.2.2 Number数据类型转换
类型转换 我们都可以称之为强制类型转换
在很多的时候 我们从其他的位置获取数据 然后获取到的数据
可能并不是我们想要的数据类型 也许是字符串 大部分时候都是字符串
所以我们要转换为数值
无论他给你的是什么数据类型 比如是字符串 那么字符串中一般也都是数值
2.2.2.1Number函数
语法:Number()
只有 Number(undefined) 会打印undefined , undefined 无法转换成数值
2.2.2.2parseInt
语法:parseInt()
parseInt是一个全局方法,它可以把值转换为整数
第1步,先解析位置0处的字符,如果不是有效数字,则直接返回 NaN.
第2步,如果位置0处的字符是数字,或者可以转换为有效数字,则继续解析位置1处的字符,如果 不是有效数字,则直接返回位置0处的有效数字。
第3步,以此类推,按从左到右的顺序,逐个分析每个字符,直到发现非数字字符为止。
第4步,parseInt()将把前面分析合法的数字字符全部转换为数值并返回。
0处有数字会解析,如果是字母直接返回NaN,向后解析时直到解析到字母会停止。
2.2.2.3 parseFloat
语法:parseFloat
// parseFloat()也是一个全局方法,它可以把值转换成浮点数,即它能够识别第一个出现的小数点,而第二个小数点视为非法。解析过程和parseInt相同。
只能保留小数第一位
2.2.2.4 运算符转换
var a = '2';
console.log(typeof +a); // es6新增
console.log(typeof (a - 0));
console.log(typeof (a * 1));
console.log(typeof (a / 1));
2.3 Boolean
这个变量只有2个结果的时候 我们可以使用boolean 布尔类型
eg:性别 选中未选中
boolean类型占用内存小
, 只占用数字类型的1/8
2.3.1boolean-类型转换
使用Boolean() 函数 以及 推荐使用 + ’ ’
Boolean强制类型转换只有六种情况 变成false
-
‘’ // 空字符串
-
0
-
NaN
-
null
-
false
-
undefined
2.4 undefined
// undefined
// 声明但是没有初始化的时候 会返回的是undefined
var a;
console.log(a);
console.log(a + 1);
// null
// undefined是null的子类(派生自null)
// 判断undefined和null之前 会将他们转换为boolean类型
// undefined 和null转换为boolean的时候 返回的都是false
// 所以相等
// undefined 和 null相等
console.log(undefined == null);
2.5 null
表示为空,Boolean()转换为 false ,做运算时转换为0
2.6 NaN (not a number)
// NaN not a number 不是一个数字
// 如果你给他的数据 不是一个数字 那么就会返回NaN
// - 我们称之为算术运算符
// 因为- 号是算术运算符 所以在两端计算都应该是数
// 那么b是不能转换为具体数字的 所以就提示你是NaN
var a = 'b' - 1;
console.log(a); // NaN
// 总结:
// 除了字符串的+ 号之外 其余任何和NaN计算的结果都是NaN
// 任何和NaN的比较都是false
3. 判断数据类型
3.1 typeof
// 如果你要是判断基本数据类型的数据 那么就是用typeof
// typeof可以识别出基本类型String,、Number、Boolean、Undefined、Symbol,但是不能识别null。
把null、array、object统一归为object类型,但是可以识别出function。
console.log(typeof 123);
// typeof 123的结果是number number这个名是什么类型是字符串类型
console.log(typeof (typeof 123));
// number可以理解为提示给你的一个单词,所以这个单词用typeof来识别,就是字符串类型。也可以理解为js为你生成了一个number来告诉你类型,而这个number自己没有特殊含义,就是提示表面单词的意思,string类型。
3.2 instanceof
instanceof 不能识别出基本的数据类型
String,、Number、Boolean、Undefined、Null、Symbol
但是可以检测出引用类型,如array、object、function,同时对于是使用new声明的类型,它还可以检测出多层继承关系。
console.log('a' instanceof String);
console.log(1 instanceof Number);
console.log(true instanceof Boolean);
// undefined没有首字母大写的那个系统提供的数据
// 所以我们要使用object 但是即使我们使用了object
// 也不可以 因为不能识别基本数据类型
console.log(undefined instanceof Object);
console.log(null instanceof Object);
console.log(Symbol() instanceof Symbol);
console.log([123] instanceof Array);
console.log({} instanceof Object);
function f1(){}
console.log(f1 instanceof Function);
// instanceof能识别出引用数据类型 但是不可以识别任何的基本数据类型
3.3 constructor
null、undefined没有construstor方法,因此constructor不能判断undefined和null。
// null、undefined没有constructor方法
var a = 'abc';
console.log(a.constructor == String);
var b = 207204858;
console.log(b.constructor == Number);
var c = true;
console.log(c.constructor == Boolean);
// var d = undefined;
// console.log(d.constructor == Object); 没有这个方法
// var e = null;
// console.log(e.constructor == Object); 没有这个方法
3.4 Object.protype.toString.call
基本可以判断任何类型
console.log(Object.prototype.toString.call('abc'));
console.log(Object.prototype.toString.call(123));
console.log(Object.prototype.toString.call(undefined));
4. 数组
4.1 数组转字符串
4.1.1 toString() 方法
toString() 在把数组转换成字符串时,首先要将数组的每个元素都转换为字符串。当每个元素都被转换为字符串时,才使用逗号进行分隔,以列表的形式输出这些字符串。
let arr = ['red', 'green', 'blue', 'pink'];
let str = '';
str = arr.toString();
console.log(typeof str, str);
4.1.2 toLocaleString()
toLocalString() 方法与 toString() 方法用法基本相同,主要区别在于 toLocalString() 方法能够使用用户所在地区特定的分隔符把生成的字符串连接起来,形成一个字符串。
str = arr.toLocaleString();
4.1.3 join()
join() 方法可以把数组转换为字符串,不过它可以指定分隔符。在调用 join() 方法时,可以传递一个参数作为分隔符来连接每个元素。如果省略参数,默认使用逗号作为分隔符,这时与 toString() 方法转换操作效果相同。
let arr = ['red', 'green', 'blue', 'pink'];
let str = '';
str = arr.join()
str = arr.join('');
str = arr.join('=');
console.log(typeof str, str);
4.2数组的遍历
4.2.1 for
最简单的一种循环遍历方法,也是使用频率最高的一种
let arr = ['apple', 'banana', 'orange'];
let len = arr.length;
for (let i = 0; i < len; i++) {
console.log(i,arr[i]);
}
// 0 apple
// 1 banana
// 2 orange
4.2.2 for…in…
这个循环用的人也很多,但是效率最低(输出的 key 是数组索引),如果遍历的是对象,输出的则是对象的属性名
let arr = ['apple', 'banana', 'orange'];
for(let key in arr) { //key中是索引
console.log(key,arr[key]);
}
// 0 apple
// 1 banana
// 2 orange
4.2.3 for…of…(ES6)
虽然性能要好于
for..in...
,但仍然比不上普通的for
循环 注意:不能循环对象,因为任何数据结构只要部署Iterator
接口,就可以完成遍历操作,有些数据结构原生具备Iterator 接口,比如Array
、Map
、Set
、**String
***等,而Iterator
**接口是部署在数据结构的Symbol.iterator
属性上的,而对象Object
恰恰是没有Symbol.iterato
属性的,所以无法被for..of
**遍历
let arr = ['apple', 'banana', 'orange'];
for(let key of arr) { // key中是值
console.log(key);
}
// apple
// banana
// orange
4.2.4 forEach
除了抛出异常以外,没有办法中止或跳出
forEach()
循环。如果你需要中止或跳出循环,forEach()
方法不是应当使用的工具。返回值为undefined
let arr = ['apple', 'banana', 'orange'];
arr.forEach((item,index,arr) => {
console.log(item,index);
})
// apple 0
// banana 1
// orange 2
item
:数组中正在处理的当前元素。
index
:数组中正在处理的当前元素的索引。
array
:forEach() 方法正在操作的数组。
不能使用return , 不会改变原数组
4.2.5 map() 函数
map函数,遍历数组每个元素,并回调操作,需要返回值,返回值组成新的数组,原数组不变。
let array = [1, 2, 3, 4, 5];
let Array = [];
Array = array.map((item) => {
return item *= 2
});
console.log(Array); // [2, 4, 6, 8, 10]
map((element, index, array) => { /* … */ }) element:元素的值 index:元素的索引 array:被遍历的数组本身
map
不会改变原数组。返回一个新数组,每个元素都是回调函数的返回值。用map时改变原数组时需要定义一个新数组接收。
4.2.6 filter() 函数
filter()
为数组中的每个元素调用一次callbackFn
函数,并利用所有使得callbackFn
返回 true 或等价于 true 的值的元素创建一个新数组。callbackFn
只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过callbackFn
测试的元素会被跳过,不会被包含在新数组中。
遍历数组,过滤出符合条件的元素并返回一个新数组,没有符合条件的元素则返回空数组
let arr = [1, 2, 0, 0, 3, 4, 0, 5, 6, 0, 7, 8, 9];
let len = arr.length;
let Array = [];
Array = arr.map(item => item != 0)
console.log(Array); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
filter(function(element, index, array) { /* … */ }, thisArg) element:元素的值 index:元素的索引 array:被遍历的数组本身
不会改变原数组
4.2.7 some(ES6)
遍历数组中是否有符合条件的元素,返回值为Boolean值。这个它只要找到**
一个
符合条件的,就返回true
,否则返回false
**如果用一个空数组进行测试,在任何情况下它返回的都是**
false
**一旦return 有满足条件的数据 就会返回true 否则就会返回false 并且当返回true之后 后面的数据就不再判断了
let arr = [
{ id: 001, name: 'ling', done: false },
{ id: 002, name: 'ling', done: false },
{ id: 003, name: 'ling', done: true },
];
let Array = arr.some(item => item.done);
console.log(Array); // true
some(function(element, index, array) { /* … */ }, thisArg) element:元素的值 index:元素的索引 array:被遍历的数组本身
不会改变原数组
4.2.8 every (ES6)
遍历数组中是否有符合条件的元素,返回值为Boolean值。全部都符合条件,则返回**
true
,否则返回false
**若收到一个空数组,此方法在任何情况下都会返回
true
。
let arr = [
{ id: 001, name: 'ling', done: false },
{ id: 002, name: 'ling', done: false },
{ id: 003, name: 'ling', done: true },
];
let Array = arr.every(item => item.done);
console.log(Array); // false
不会改变原数组
4.2.9 find (ES6)
遍历数组,返回符合条件的第一个元素,如果没有符合条件的元素则返回 undefined
let arr = [1, 2, 3, 4, 5];
let element = arr.find(item => item == 3);
console.log(element);
不会改变原数组
4.2.10 findIndex
findIndex()
方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。
let arr = [1, 2, 3, 4, 5];
let element = arr.find(item => item == 3);
console.log(element);
不会改变原数组
4.2.11 reduce
reduce()
方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
let arr = [1,2,3,4,5,6,7,8,9];
let sum = 0;
sum = arr.reduce((pre,current) => pre + current);
console.log(sum); // 45
reduce((previousValue, currentValue, currentIndex, array) => { /* … */ }, initialValue) previousValue:上一次调用 callbackFn 时的返回值。在第一次调用时,若指定了初始值 initialValue,其值则为 initialValue,否则为数组索引为 0 的元素 array[0]。 currentValue:数组中正在处理的元素。在第一次调用时,若指定了初始值 initialValue,其值则为数组索引为 0 的元素 array[0],否则为 array[1]。 currentIndex:数组中正在处理的元素的索引。若指定了初始值 initialValue,则起始索引号为 0,否则从索引 1 起始。 array:用于遍历的数组。 initialValue 可选 作为第一次调用 callback 函数时参数 previousValue 的值。若指定了初始值 initialValue,则 currentValue 则将使用数组第一个元素;否则 previousValue 将使用数组第一个元素,而 currentValue 将使用数组第二个元素。
**
initialValue
**可以传入数组实现数组去重,也可以传入起始值实现不同的功能
4.3 数组的增删改查
4.3.1 增 (源码分析)
4.3.1.1 从尾部增加
arr[arr.length]
// 0 1 2 3 4 5
var arr = [1, 2, 3, 4, 5];
// 如何往数组的末尾添加数据 我们可以利用数组的长度
// 因为数组的下标是从0开始的 我们获取的长度 永远都是下一个元素
arr[arr.length] = 6;
console.log(arr);
4.3.1.2 从头部添加
var arr = [1, 2, 3, 4, 5];
let len = arr.length;
for (let i = len - 1; i >= 0; i--) {
arr[i + 1] = arr[i];
}
arr[0] = 6;
console.log(arr); // [6,1,2,3,4,5]
4.3.1.3 从中间增加
只需要修改 i >= ( 这个数字 ) 即可,这个写法只是把我们客观认为的第几项作为插入的第几位
let num = prompt('从第几位插入');
let number = Number(prompt('请输入数字'));
let array = [1, 2, 3, 4, 5, 6, 7];
for (let i = array.length - 1; i >= num - 1; i--) {
array[i + 1] = array[i];
}
array[num - 1] = number;
console.log(array);
4.3.1.4 push() 方法
push()
方法将一个或多个元素
添加到数组的末尾,并返回该数组的新长度。
let arr = [1, 2, 3, 4, 5];
let a = arr.push(6,7,8,9,10);
console.log(arr, a); // [1,2,3,4,5,6,7,8,9,10] 10
4.3.1.5 unshift() 方法
unshift()
方法将一个或多个元素
添加到数组的开头,并返回该数组的新长度。
let arr = [5, 6, 7, 8, 9, 10];
let a = arr.unshift(1, 2, 3, 4); // a = 10
console.log(arr, a); // [1,2,3,4,5,6,7,8,9,10] 10
4.3.2 删
4.3.2.1 从尾部删除
修改数组长度从而删除尾部的元素
尾部删除
let arr = [1,2,3,4,5];
arr.length--;
console.log(arr);
4.3.2.2 从头部删除
让后边的元素依次覆盖前面的元素,使元素整体前移一个长度,再length-- 即可
从头部删除
let arr = [1,2,3,4,5];
let len = arr.length;
for (let i = 0; i < len - 1; i++) {
arr[i] = arr[i + 1];
}
arr.length--;
console.log(arr); // [2,3,4,5]
4.3.2.3 从中间删除
修改一下 i 的起始值即可
从中间删除
let arr = [1,2,3,4,5];
let num = prompt('请输入要删除第几项');
let len = arr.length;
for (let i = num; i < len - 1; i++) {
arr[i] = arr[i + 1];
}
arr.length--;
console.log(arr); //
4.3.2.4 shift() 方法
shift()
方法从数组中删除**第一个元素
**,并返回该元素的值。此方法更改数组的长度。
let arr = [1, 2, 3, 4, 5];
let a = arr.shift(); // a = 1
console.log(arr, a); // [2,3,4,5] 1
4.3.2.5 pop() 方法
pop()
方法从数组中删除**最后一个元素
**,并返回该元素的值。此方法会更改数组的长度。
let arr = [1, 2, 3, 4, 5];
let a = arr.pop(); // a = 5
console.log(arr, a); // [1,2,3,4] 5
4.3.2.6 slice()
slice()
方法返回一个新的数组对象,这一对象是一个由start
和end
决定的原数组的浅拷贝(包括begin
,不包括end
)。原始数组不会被改变。
let arr = [1,2,3,4,5];
// let array = arr.slice(2,4); // array = [3,4]
let array = arr.slice(1,-1); // array = [2,3,4]
console.log(array, arr);
slice(start, end) start: 起始位置,如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取。数值超出原数组的索引范围,则会返回空数组。 end: 终止位置,如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。如果 end 被省略或者大于数组长度,则会一直提取到原数组末尾。
4.3.3 改
let arr = [1,2,3,4,5];
arr[5] = 6;
console.log(arr); // [1,2,3,4,5,6]
4.3.3.1splice() 可以增删改
splice()
方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。返回值:由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。
// 只写一个起始值
let arr = [1,2,3,4,5];
let array = arr.splice(2);
console.log(array); // [3,4,5]
// 写两个值
let arr = [1,2,3,4,5];
let array = arr.splice(2,2);
console.log(array); // [3,4]
// 三个值
let arr = [1,2,3,4,5];
let array = arr.splice(2,2,6,6);
console.log(array,arr); // [3,4] [1,2,6,6,5]
splice(start, deleteCount, item1, item2, itemN) start 指定修改的开始位置(从 0 计数)。如果超出了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组末位开始的第几位;如果负数的绝对值大于数组的长度,则表示开始位置为第 0 位。 deleteCount 整数,表示要移除的数组元素的个数。省略或者数值大于start之后的元素的总数就会删除开始位置之后的全部元素。 item 要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。
4.3.4 查
var arr = [1,2,3,4,5];
console.log(arr[0]); // 1
4.3.4.1 indexOf()
indexOf(searchElement, fromIndex) searchElement 要查找的元素 fromIndex: 开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回 -1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即 -1 表示从最后一个元素开始查找,-2 表示从倒数第二个元素开始查找 ,以此类推。 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于 0,则整个数组都将会被查询。其默认值为 0。
let arr = [1,2,3,4,5,6,1,2,5];
console.log(arr.indexOf(1)); // 0
console.log(arr.indexOf(1, 1)); // 6
返回首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1
4.4 影响原数组的方法
push
push()
方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度
var arr = [1,2];
var result = sports.push(3);
console.log(arr); // [1,2,3]
console.log(result); // 3 返回数组长度
push (element0, element1, /* … ,*/ elementN) elementN 被添加到数组末尾的元素
pop
pop()
方法从数组中删除最后一个元素,并返回该元素的值。此方法会更改数组的长度。参数只能写一个
const arr = [1,2,3,4];
const result = myFish.pop();
console.log(arr); // [1,2,3]
console.log(result); // 4
unshift
unshift()
方法将一个或多个元素添加到数组的头部,并返回该数组的新长度
var arr = [1,2];
var result = sports.push(3);
console.log(arr); // [3,1,2]
console.log(result); // 3 返回数组长度
unshift (element0, element1, /* … ,*/ elementN) elementN 被添加到数组末尾的元素
shift
shift()
方法从数组中删除第一个一个元素,并返回该元素的值。此方法会更改数组的长度。参数只能写一个
const arr = [1,2,3,4];
const result = myFish.pop();
console.log(arr); // [2,3,4]
console.log(result); // 1
splice
splice()
方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb'); // expected output: Array ["Jan", "Feb", "March", "April", "June"]
months.splice(4, 1, 'May'); // expected output: Array ["Jan", "Feb", "March", "April", "May"]
splice(start, deleteCount, item1, item2, itemN)
start
修改的开始位置
deleteCount
可选 整数,表示要移除的数组元素的个数。
item1, item2, ...
可选 要添加进数组的元素,从start
位置开始。如果不指定,则splice()
将只删除数组元素。
reverse
reverse()
方法将数组中元素的位置颠倒,并返回该数组。请注意,数组已原地排序,并且不进行复制。
let arr = [1,2,3];
arr.reverse(); // [3,2,1]
sort
sort()
方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序列时构建的
*箭头函数 ** sort((a, b) => { / … */ } ) 如果没有指明
compareFn
,那么元素会按照转换为的字符串的诸个字符的 Unicode 位点进行排序。a 第一个用于比较的元素。 b 第二个用于比较的元素。
a - b 从小到大 b - a 从大到小
const numbers = [4, 2, 5, 1, 3];
numbers.sort(function (a, b) {
return a - b; // 从小到大
});
console.log(numbers); // [1, 2, 3, 4, 5]
4.5 不影响原数组的方法
4.5.1 join
join()
方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。 如果arr.length
为 0,则返回空字符串。
const elements = ['Fire', 'Air', 'Water'];
console.log(elements.join());
// expected output: "Fire,Air,Water"
console.log(elements.join(''));
// expected output: "FireAirWater"
console.log(elements.join('-'));
// expected output: "Fire-Air-Water"
//如果数组只有一个项目,那么将返回该项目而不使用分隔符。
join ( separator )
separator
可选指定一个字符串来分隔数组的每个元素。如果缺省该值,数组元素用逗号(
,
)分隔。如果separator
是空字符串 (""
),则所有元素之间都没有任何字符。
4.5.2 concat
concat()
方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
let arr1 = [1,2,3,4,5];
let arr2 = [5,6,7,8,9];
let arr3 = arr1.concat(arr2);
console.log(arr3); // [1,2,3,4,5,6,7,8,9]
合并多个数组用逗号分隔
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let arr3 = [7, 8, 9];
let arr = arr1.concat(arr2, arr3);
console.log(arr); // [1,2,3,4,5,6,7,8,9]
4.5.3 slice
slice()
方法返回一个新的数组对象,这一对象是一个由begin
和end
决定的原数组的浅拷贝(包括begin
,不包括end
)。原始数组不会被改变。
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]
console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]
console.log(animals.slice(1, 5));
// expected output: Array ["bison", "camel", "duck", "elephant"]
console.log(animals.slice(-2));
// expected output: Array ["duck", "elephant"]
console.log(animals.slice(2, -1));
// expected output: Array ["camel", "duck"]
slice(start, end)
begin
提取起始处的索引end
提取终止处的索引 ()中不写,就是浅拷贝,返回整个数组
4.5.4 fill
fill()
方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
const array1 = [1, 2, 3, 4];
console.log(array1.fill(0, 2, 4)); [1, 2, 0, 0]
fill ( value, start, end )
value 用来填充数组元素的值
start 起始索引,默认值为 0
end 终止索引,默认值为
arr.length
4.5.5 valueOf
valueOf()
方法返回指定对象的原始值。
对象 | 返回值 |
---|---|
Array | 返回数组对象本身。 |
Boolean | 布尔值。 |
Date | 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。 |
Function | 函数本身。 |
Number | 数字值。 |
Object | 对象本身。这是默认情况。 |
String | 字符串值。 |
Math 和 Error 对象没有 valueOf 方法。 |
4.5.6 toString
toString()
方法返回一个字符串,表示指定的数组及其元素。
const array1 = [1, 2, 'a', '1a'];
console.log(array1.toString()); // "1,2,a,1a"
toString ( ) 返回一个表示数组所有元素的字符串。( 数组转字符串 )
4.5.7 includes
includes()
方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true
,否则返回false
。
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
let array = [];
arr.forEach((item,index) => array.includes(item) ? '' : array[array.length] = item)
console.log(array); // [1,2,3,4,5]
includes ( searchElement, fromIndex ) searchElement: 需要查找的元素值。 fromIndex: 从什么位置开始查找
4.5.8 lastIndexOf
lastIndexOf()
方法返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从fromIndex
处开始。
let arr = [1,2,3,4,2];
console.log(arr.lastIndexOf(2)); // 4
4.6 数组去重
4.6.1 利用新数组来实现去重
4.6.1.1
let arr = [1,1,2,2,3,3,4,4,5,5];
let array = [];
for (let i = 0; i < arr.length; i++) {
let flag = true;
for (let j = 0; j < array.length; j++) {
if (arr[i] == array[j]) {
flag = false;
break;
}
}
if (flag) {
array[array.length] = arr[i];
}
}
console.log(array);
4.6.1.2 indexOf
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
let array = [];
for (let i = 0; i < arr.length; i++) {
if (array.indexOf(arr[i]) == -1) {
array[array.length] = arr[i];
}
}
console.log(array);
4.6.1.3 for in
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
let array = [];
for (let key in arr) {
if (key == arr.indexOf(arr[key])) {
array[array.length] = arr[key];
}
}
console.log(array);
4.6.1.4 forEach
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
let array = [];
arr.forEach((item,index) => arr.indexOf(item) == index ? array[array.length] = item : '')
console.log(array);
4.6.1.5 filter
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
let array = [];
array = arr.filter((item,index) => arr.indexOf(item) == index)
console.log(array);
4.6.1.6 includes
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
let array = [];
arr.forEach((item,index) => array.includes(item) ? '' : array[array.length] = item)
console.log(array);
4.6.1.7 Set
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
let array = [...new Set(arr)];
console.log(array);
4.6.2 不使用新数组实现去重
4.6.2.1 前
let arr = [1,1,2,2,3,3,4,4,5,5];
for (let i = 0; i < arr.length; i++) {
for(let j = 0; j < i; j++) {
if (arr[i] == arr[j]) {
for (let z = i; z < arr.length -2; z++) {
arr[z] = arr[z + 1];
}
arr.length--;
i--;
}
}
}
console.log(arr);
4.6.2.2 后
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) {
for (let z = i; z < arr.length - 2; z++) {
arr[z] = arr[z + 1];
}
arr.length--;
i--;
}
}
}
console.log(arr);
4.6.2.3 splice
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) {
arr.splice(i, 1);
}
}
}
console.log(arr);
5. 字符串
5.1 字符串转数组
字符串方法 | 说明 |
---|---|
split() 方法 | 将字符串转换成一个数组 |
扩展运算符 (…) | es6里面的扩展运算符 |
5.1.1 split
split() 方法用于把一个字符串分割成字符串数组
spllit()方法 | 说明 |
---|---|
split(‘,’) | 可理解为直接变成字符串,默认逗号分隔 |
split(’ ') 空字符串 | 每个字符之间都会被分割 |
let Array = 'red,green,blue,pink';
console.log(Array.split(','));
5.1.2 …(扩展运算符)
let Array = 'red,green,blue,pink';
console.log(Array.split(','));
5.2 字符串方法
5.2.1 toLowerCase
toLowerCase 将大写字母转换为小写 (根据unicode码)
var str = 'ABcd';
console.log(str.toLowerCase()); // abcd
5.2.2 toUpperCase
toUpperCase 将小写字母转换为大写 (根据unicode码)
var str = 'abcd';
console.log(str.toUpperCase()); // ABCD
####5.2.3 charAt
charAt 根据下标找字符 默认从0开始
功能 | 参数 | 返回值 |
---|---|---|
找到对应索引位置的字符 | 一个索引值 默认从0开始 | 返回找到的对应字符 |
var str = 'hello';
console.log(str.charAt(0)); // h
console.log(str.charAt(1)); // e
console.log(str.charAt(2)); // l
5.2.4 charCodeAt
charCodeAt 找到对应字符串中对应位置的Unicode码
功能 | 参数 | 返回值 |
---|---|---|
找到对应索引位置的字符的Unicode码 | 一个索引值 | 返回找到的对应字符的Unicode码 |
var str = '李晶苏';
console.log(str.charCodeAt(0)); // 26446
console.log(str.charCodeAt(1)); // 26230
console.log(str.charCodeAt(2));
5.2.5 fromCharCode
功能 | 参数 | 返回值 |
---|---|---|
把unicode码转化为对应的字符 | 指定一个Unicode码 | 返回对应的字符 |
console.log(String.fromCharCode(26446)); // 李
console.log(String.fromCharCode(26230)); // 晶
console.log(String.fromCharCode(33487));
5.2.6 indexOf
功能 | 参数 | 返回值 |
---|---|---|
从原串当中找指定的字串,求出字串下标的位置 | 1、指定的一个子串 2、起始位置从哪开始查找 默认时从左边第一个开始往右 | 返回对应的字符串下标,如果没有找到返回 -1 |
indexOf 根据字符来找到字符在字符串中的下标位置
var str = "chinai";
console.log(str.indexOf("i")); // 2
console.log(str.indexOf("i")); // 2
// 如果找不到字串 如果找到就返回对应位置的下标 如果找不到就返回 -1
console.log(str.indexOf("b")); // -1
// 如果参数是一个空字符串 那么返回的结果是0
console.log(str.indexOf("")); // 0
5.2.7 lastIndexOf
这个和indexOf类似,只不过这个是从右往左查
var str = "beautiful";
console.log(str.lastIndexOf("u")); // 7
5.2.8 concat
功能 | 参数 | 返回值 |
---|---|---|
把原串和指定的字符串拼接到一起 | 指定一个新的字符串 | 返回拼接好的字符串 |
var str = 'hello';
console.log(str.concat(' world')); // hello world
5.2.9 localeCompare
功能 | 参数 | 返回值 |
---|---|---|
比较原串和指定的字符串大小 | 指定一个字符串 | 如果原串大返回 1 如果原串小返回 -1 如果相等返回 0 |
var str = '5';
console.log(str.localeCompare('6')); // -1
console.log(str.localeCompare('4')); // 1
console.log(str.localeCompare('5')); // 0
5.2.10 slice
功能 | 参数 | 返回值 |
---|---|---|
从原串当中截取指定位置(索引)的字符串,形成新串 | 指定起始位置和结束位置,位置可以是负数,包含起始位置的但是不包含结束位置的 参数也可以只写一个,代表起始位置,结束位置不写代表一直到字符串结束 | 返回截取的字符串 |
var str = 'hello';
// 包含起始位置的但是不包含结束位置的
console.log(str.slice(2,4));
// 位置可以是负数,
console.log(str.slice(-3,-1));
// 参数也可以只写一个,代表起始位置,结束位置不写代表一直到字符串结束
console.log(str.slice(2));
5.2.11 substr
功能 | 参数 | 返回值 |
---|---|---|
从原串当中截取的字符串,形成新串 | 指定起始位置和长度,位置也可以是负 如果只写一个起始位置,一直截取到末尾 | 返回截取的字符串 |
var str = 'tian';
// 指定起始位置和长度
console.log(str.substr(1,2));
// 位置也可以是负数,
console.log(str.substr(-3,2));
// 如果只写一个起始位置,一直截取到末尾
console.log(str.substr(2));
5.2.12 substring
//功能:从原串当中截取的字符串,形成新串
//参数:指定两个位置,两个位置不能是负数,
//方法会自动的根据位置大小决定起始和结束位置
//不包含结束位置的那个字符
//返回值:返回截取的字符串
var str = "yanqiu";
console.log(str.substring(2, 4));
5.2.13 split
功能 | 参数 | 返回值 |
---|---|---|
以指定字符为间隔(切割点)将字符串转化为数组 | 可以不写,那么整个字符串作为数组的一个元素 可以是空串,那么每个字符都会成为数组的一个元素 可以是指定的字符串,会以这个字符串为切割点去切割 如果没有这个切割点,和不传一样 | 返回生成的数组 |
var str = '1#2#3#4#5';
console.log(str.split('#')); // ['1','2','3','4','5']
console.log(str.split()); // ['1#2#3#4#5#']
console.log(str.split('')); // ['1','#','2','#','3','#','4','#','5']
console.log(str.split('$')); // ['1#2#3#4#5#']
5.2.14 toString
var str = '123';
console.log(str.toString());
5.2.15 startsWith
判断是否以指定字符串开头 (可以写多个值,必须是相连才会返回 true)
var str = 'hello';
console.log(str.startsWith('he')); // true
console.log(str.startsWith('e')); // true
5.2.16 endsWith
判断是否以指定字符串结尾 (可以写多个值,必须是相连才会返回 true)
var str = 'hello';
console.log(str.endsWith('o')); // ture
console.log(str.endsWith('a')); // false
console.log(str.endWith('lo')); // true
5.2.17 includes
判断是否包含该元素,包含返回 true,否则返回 false
var str = 'hello';
console.log(str.includes('l'));
console.log(str.includes('a'));
console.log(str.includes('eo'));
console.log(str.includes('el'));
5.2.18 repeat
语法 | 参数 | 返回值 |
---|---|---|
str.repeat(count) | 介于 0 和 +Infinity 之间的整数。表示在新构造的字符串中重复了多少遍原字符串。 | 包含指定字符串的指定数量副本的新字符串。 |
构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。
var str = 'i hate you';
console.log(str.repeat(100));
5.2.19 trim
trim()
方法会从一个字符串的两端删除空白字符。在这个上下文中的空白字符是所有的空白字符 , 以及所有行终止符字符(如 LF,CR 等)。
const greeting = ' Hello world! ';
console.log(greeting.trim()); // Hello world!
返回值 返回一个**去掉空白的新字符串 **
不影响原字符串本身
str.trimStart ( ) 去除开始空格 str.trimEnd ( ) 去除结束空格
6. 对象
6.1 对象定义
原始值
1.数值 Number
2.大整数 BigInt
3.字符串 String
4.布尔值 Boolean
5.空值 Null
6.未定义 Undefined
7.符号 Symbol
对象是JS中的一种**
复杂数据类型
**它相当于一个**
容器
,在对象中可以存储各种不同类型数据
**原始值只能用来表示一些简单的数据,不能表示复杂数据
对象中可以存储多个各种类型的数据
对象中存储的数据,我们称为属性
向对象中添加属性: 对象.属性名 = 属性值
读取对象中的属性 对象.属性名
如果读取的是一个对象中没有的属性 不会报错而是返回undefined
6.2 对象的创建方式
6.2.1 使用关键词new创建
var obj = new Object();
obj.name = "马志强";
obj.age = 70;
obj.color = "black";
obj.eat = function () {
console.log("apple");
};
6.2.2 通过工厂类创建
function getFactory(name, size, color) {
var obj = new Object();
obj.name = name;
obj.size = size;
obj.color = color;
return obj;
}
var obj1 = getFactory("李宁", 42, "green");
console.log(obj1);
6.2.3 通过构造器创建 (constructor)
- 构造方法 又叫做构造器 同时也叫做构造函数
- 构造函数的首字母建议大写
- 构造函数不要写return
- 构造函数也是必须要调用之后才可以执行
function Person(name, age) {
this.name = name;
this.age = age;
}
let p = new Person("ling", 16);
console.log(p);
6.2.4 通过字面量创建
var person1 = {
name:'张三',
age:19
}
6.3 对象的属性
6.3.1 属性名
- 通常属性名就是一个字符串,所以属性名可以是任何值,没有什么特殊要求
但是如果你的属性名太特殊了,不能直接使用,需要使用[]来设置
虽然如此,但是我们还是强烈建议属性名也按照标识符的规范命名- 也可以使用符号(symbol)作为属性名,来添加属性 获取这种属性时,也必须使用symbol
使用symbol添加的属性,通常是那些不希望被外界访问的属性
- 使用[]去操作属性时,可以使用变量
6.3.2 属性值
- 对象的属性值可以是任意的数据类型,也可以是一个对象
使用typeof检查一个对象时,会返回object。检查引用数据类型使用instanceof
6.3.3 for…in 循环中的代码块会为每个属性执行一次
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>for in测试</title>
</head>
<body>
<script>
var person = {name:"Barry", age:18};
for(var x in person) {
console.log(x,person[x]);
}
</script>
</body>
</html>
6.4 创建对象的方式
let obj = new Object();
let obj = Object();
let obj = {};
对象字面量 可以直接使用{} 来创建对象 [ ]中可以写变量
语法: {
属性名:属性值,
/ [属性名]:属性值,
}
6.5 枚举属性 for-in语句
语法:
for(let propName in 对象){
语句…
}
let obj = {
name:'孙悟空',
age:18,
gender:"男",
address:"花果山",
[Symbol()]:"测试的属性" // 符号添加的属性是不能枚举
}
for(let propName in obj){
console.log(propName, obj[propName])
}
for-in的循环体会执行多次,有几个属性就会执行几次,
每次执行时,都会将一个属性名赋值给我们所定义的变量注意:并不是所有的属性都可以枚举,比如:使用符号添加的属性
6.6 可变类型
-
原始值都属于不可变类型,一旦创建就无法修改
-
在内存中不会创建重复的原始值
-
对象属于可变类型
-
对象创建完成后,可以任意的添加删除修改对象中的属性
-
注意:
- 当对两个对象进行相等或全等比较时,比较的是对象的内存地址
- 如果有两个变量同时指向一个对象,通过一个变量修改对象时,对另外一个变量也会产生影响
// let obj = {name:"孙悟空"}
let obj = Object()
obj.name = "孙悟空"
obj.age = 18
let obj2 = Object()
let obj3 = Object()
// console.log(obj2 == obj3) // false
let obj4 = obj
obj4.name = "猪八戒" // 当修改一个对象时,所有指向该对象的变量都会收到影响
console.log("obj", obj.name) // obj 猪八戒
console.log("obj4", obj4) // obj4 猪八戒
console.log(obj === obj4) // true
6.7 修改对象
修改对象
-
修改对象时,如果有其他变量指向该对象 则所有指向该对象的变量都会受到影响
-
修改变量
-
修改变量时,只会影响当前的变量
在使用变量存储对象时,很容易因为改变变量指向的对象,提高代码的复杂度
所以通常情况下,声明存储对象的变量时会使用const
注意:const只是禁止变量被重新赋值,对对象的修改没有任何影响
-
const obj = {
name: "孙悟空",
}
const obj2 = obj; // 传递的内存地址,改变obj2中的属性会影响obj
// obj2 = {}; // 重新赋值,重新开辟一个内存地址
obj2.name = "猪八戒"; // 修改对象 影响指向它的所有对象
// obj2 = null; // 修改变量
// console.log(obj);
// console.log(obj2);
const obj3 = {
name:"猪八戒";
}
obj3.name = "沙和尚";
console.log(obj3);
6.8 存储方法
``方法(method)`
- 当一个对象的属性指向一个函数,
那么我们就称这个函数是该对象的方法
调用函数就称为调用对象的方法
let obj = {
name: 'ling',
say:function() {
console.log('name: ' + this.name);
}
}
obj.say(); // name: ling
7. 函数
7.1 预解析
预解析 只能解析 带var的变量 和 function定义的函数
预解析 包含了变量提升 和 函数提升
变量提升
1. 变量提升
提升的是变量的声明
console.log(a);
var a = 1;
console.log(a);
预解析的结果
var a;
console.log(a);
a = 1;
console.log(a);
函数提升(第一种定义方式)
f1();
function f1(){
console.log('十一三天乐');
}
// 预解析的结果
function f1(){
console.log('十一三天乐');
}
f1();
表达式定义
f1();
var f1 = function(){
console.log(333);
}
// 预解析的结果
var f1;
f1();
f1 = function(){
console.log(333);
}
(1)如果函数重名,会覆盖(后面的函数会把前面的覆盖掉)
f1();
function f1(){
console.log('睡觉');
}
function f1(){
console.log('洗澡');
}
(2)如果变量(指的是变量和函数)重名,会忽略变量
console.log(f1);
var f1 = 1;
function f1(){
console.log(1111);
}
console.log(f1);
预解析的结果
function f1(){
console.log(1111);
}
var f1;
console.log(f1);
f1 = 1;
console.log(f1);
7.2 函数的声明方式
- function fun()
- let fn = function() {}
- let fn1 = () => {}
7.3 函数的参数
在函数调用的时候 将形参传给了实参
-
实参: 实际参数 就是在函数调用的时候 我们传递过去的真真正正的值
-
形参: 形式参数 就是在定义函数的时候 写在 () 中的变量
-
它的值 完全取决于 实参
function fn(name,age) { // 形参
// console.log(name,age);
return (name + ' ' + age)
}
let result = fn('ling',18); // 实参
console.log(result);
7.3.1获取实参
语法:使用 arguments
function f1() {
console.log(arguments);
};
f1(1,2,3,4,5);
7.3.2 获取形参
语法:函数名.length
function f1(a,b,c,d) {
console.log(f1.length);
};
f1();
7.3.3 实参和形参的对应关系
(1)实参和形参一一对应
function f1(a, b) {
console.log(a, b); // 1 2
}
f1(1, 2);
(2)实参的个数多于形参
function f2(a,b) {
console.log(a, b); 1 2
}
f2(1,2,3);
(3)实参的个数少于形参
function f3(a,b) {
console.log(a, b); 1 undefined
}
f3(1);
7.4 返回值
如果函数没有写 return , 那么返回值是undefined
function f1(){
}
var result = f1();
console.log(result);
如果函数写return 并且return还有值 , 返回的就是那个值 , return 之后的代码不会执行
function f3(){
return '想吃火锅';
console.log('111'); // return 之后不执行
}
var result = f3();
console.log(result);
7.5 作用域
7.5.1 局部作用域
局部作用域
-
局部变量:在函数内部定义的变量 我们称之为局部变量
-
局部变量 生效的范围我们称之为局部作用域
-
局部变量 只能在函数内部使用 不可以在函数外部使用
-
形参也可以当作一种特殊的局部变量
function f1() {
var a = 1;
console.log(a); // 1
}
f1();
function f1(a) {
console.log(a);
}
console.log(a); // 报错
f1(111);
7.5.2 全局作用域
全局作用域:
- 全局变量:在函数外部定义的变量 我们称之为全局变量
- 全局变量生效的范围就是全局作用域
- 全局变量 可以在任意位置使用 包含函数外部 也包含函数内部
var a = 1; // 全局变量
console.log(a);
function f1() {
console.log(a);
}
f1();
7.6 特殊的全局变量
// 在函数内部定义的变量 如果没有加var 那么该变量就是全局变量
function f1() {
a = 1; // var a = 1
}
f1();
console.log(a); // 1 不加var和let直接挂载到window上
7.7 闭包(最重要)
第一种写法
function f1() {
var a = 1;
function f2() {
return a;
}
return f2;
}
// 我调用f1 那么返回的是f2的函数体
var result = f1(); // result中的就是f1函数的返回值,f2函数体
// 函数体加上一个() 那么就执行函数了
var bbb = result(); // 执行f2函数
console.log(bbb); // 打印f2函数的返回值
第二种写法
function f1() {
var a = 1;
return function() {
return a;
}
}
var result = f1();
var bbb = result();
console.log(bbb);
换成箭头函数
function fn() {
var a = 4;
return () => a;
}
let result = fn();
console.log(result());
7.7.1总结
(1) 什么是闭包
闭包是一种引用关系 该引用关系存在于内部函数中 引用的是外部函数的局部变量的对象、
(2) 闭包的产生条件
a. 在外部函数内定义一个内部函数
b. 在内部函数引用外部函数的局部变量
c. 外部函数返回内部函数的函数体
d. 外部函数和内部函数都要使用
(3) 闭包的作用
闭包延长了 外部函数的局部变量的生命周期
注意事项:
闭包主要用来隐藏一些不希望被外部访问的内容
这就意味着闭包需要占用一定的内存空间
相较于类来说,闭包比较浪费内存空间(类可以使用原型而闭包不能)
需要执行次数较少时,使用闭包
需要大量创建实例时,使用类
function buyBag() {
var money = 800;
function f2() {
money -= 100;
return money;
}
return f2;
}
var result = buyBag();
var a = result();
console.log(a);
var b = result();
console.log(b);
var c = result();
console.log(c);
如果没有闭包 那么局部变量 在每次调用函数的时候都会被初始化
因为函数在执行完毕之后 所以的局部变量将被销毁
闭包延长了 外部函数的局部变量的生命周期
正常的函数 在代码执行完毕之后 就被删除了
7.7.2 经典面试题
function fn() {
var a = 5;
function fn1() {
a--;
console.log(a);
}
return fn1;
}
// fn()(); 会重新赋值,也会重新生成fn1 都是输出4
// fn()(); 会重新赋值,也会重新生成fn1
// fn()(); 会重新赋值,也会重新生成fn1
7.7.3 闭包的缺点
因为闭包的特殊性 会导致内存无法释放 也就是内存泄漏
如果内存泄漏 就会导致内存溢出
解决内存溢出 或者 内存泄漏的问题
function f1(){
var a = 5;
function f2(){
a--;
console.log(a);
}
return f2;
}
var result = f1();
result();
result();
result();
result = null; // 解决内存泄漏 result = null;
result();
7.8 普通函数与箭头函数this
函数在执行时,JS解析器每次都会传递进一个隐含的参数 这个参数就叫做 this this会指向一个对象
语法格式 | new和原型 | arguments | this指向 | call,apply和bind |
---|---|---|---|---|
function(){} 函数声明 函数表达式 | 有 | 有 | this所指向的对象会根据函数调用方式的不同而不同 1.以函数形式调用时,this指向的是window 2.以方法的形式调用时,this指向的是调用方法的对象 | 修改this值 |
()=> {} 函数表达式 | 没有 | 没有,可以调用外围 | 在箭头函数定义时就决定的,而且不可修改的。指向定义的时候、外层第一个普通函数的this | 不可修改this值 |
1、this指向的问题
箭头函数的this在箭头函数定义时就决定了,而且不可修改的 (call、apply、bind)。
箭头函数的 this 是一个普通变量,指向了父级函数的 this,且这个指向永远不会改变,也不能改变。
2、箭头函数不能new (不能当作构造函数)
3、箭头函数没有prototype
4、箭头函数没有arguments
let obj = {
name: 'ling',
say() {
let fn = () => {
console.log(this); // fn的this定义时已经确定,就是外层的say的this指向,say的this指向obj
};
fn();
function fn1() {
console.log(this); // 普通函数以函数形式调用,this指向window
};
fn1();
}
}
obj.say();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b8kj4L9R-1665326920515)(C:\Users\24601\AppData\Roaming\Typora\typora-user-images\image-20220930140721575.png)]
7.8.1 this的6种情况
在script标签中 this代表的是window对象
在普通函数中 this代表的是window对象
function f1(){
console.log(this);
}
f1();
在构造函数中 this代表的是实例化的那个对象
function Person(){
console.log(this);
}
new Person();
在方法中 this代表的是方法的调用者
var person = {
name:'zs',
eat:function(){
console.log(this);
}
}
person.eat();
在事件回调函数中 this代表的是事件源
事件源:事件的源头 也就是你现在正在操作谁 那个谁就是事件源
var btn = document.getElementById('btn');
btn.onclick = function(){
console.log(this);
}
在call和apply中 this代表的是第一个参数
7.9 递归
阶乘
function testFac(n) {
if (n == 1) {
return 1;
}
return n * testFac(n - 1);
}
var result;
斐波那契数列
function fb(n) {
if (n == 1 || n == 2) {
return 1;
}
return fb(n - 1) + fb(n - 2);
}
console.log(fb(6));
使用callee解决命名污染
var n = 1;
function f1() {
if (n > 10) {
return;
}
console.log("ling");
n++;
f1 = 11111;
arguments.callee();
}
f1();
8. 面向对象
8.1 面向对象编程
面向对象编程(OOP)
1. 程序是干嘛的?
- 程序就是对现实世界的抽象(照片就是对人的抽象)
对象是干嘛的?
-
一个事物抽象到程序中后就变成了对象
-
在程序的世界中,一切皆对象
- 面向对象的编程
-
面向对象的编程指,程序中的所有操作都是通
过对象来完成 -
做任何事情之前都需要先找到它的对象,然后
通过对象来完成各种操作
8.2堆栈区别
栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型和引用数据类型的变量
所有在函数/方法中定义的变量都是放在栈内存中,随着函数/方法的执行结束,这个函数/方法的内存栈也自然销毁。
优点:存取速度比堆快。
缺点:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
堆(heap):动态分配的内存,大小不定也不会自动释放,存放引用类型的对象,指那些可能由多个值构成的对象,保存在堆内存中。
堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(参数传递)。创建对象是为了反复利用。
8.3 类
使用Object创建对象的问题:
1.无法区分出不同类型的对象
2.不方便批量创建对象
在JS中可以通过类(class) 来解决这个问题
1.类是对象的模板,可以将对象中的属性和方法直接定义到类中
定义后,就可以直接通过类来创建对象
2.通过同一个类创建的对象称为同类对象
可以使用instanceof来检查一个对象是否由某个类创建
如果某个对象是有某个类创建,则我们称该对象是这个类的实例
语法:
class 类名 {} // 类名要使用大驼峰命名
const 类名 = class {}
通过类创建对象
new 类()
8.4 构造函数
class Person{
// 在类中可以添加一个特殊的方法constructor
// 该方法我们称为构造函数(构造方法)
// 构造函数会在我们调用类创建对象时执行
constructor(name, age, gender){
// console.log("构造函数执行了~", name, age, gender)
// 可以在构造函数中,为实例属性进行赋值
// 在构造函数中,this表示当前所创建的对象
this.name = name
this.age = age
this.gender = gender
}
}
8.5 封装
面向对象的特点:
封装、继承和多态
- 封装
- 对象就是一个用来存储不同属性的容器
- 对象不仅存储属性,还要负责数据的安全
- 直接添加到对象中的属性,并不安全,因为它们可以被任意的修改
- 如何确保数据的安全:
-
私有化数据
-
将需要保护的数据设置为私有,只能在类内部使用
2.提供setter和getter方法来开放对数据的操作 -
属性设置私有,通过getter setter方法操作属性带来的好处
- 可以控制属性的读写权限
- 可以在方法中对属性的值进行验证
-
封装主要用来保证数据的安全
-
实现封装的方式:
1.属性私有化 加#
2.通过getter和setter方法来操作属性
get 属性名(){
return this.#属性
}
set 属性名(参数){
this.#属性 = 参数
}
-
class Person {
// #address = "花果山" // 实例使
#name
#age
#gender
constructor(name, age, gender) {
this.#name = name
this.#age = age
this.#gender = gender
}
sayHello() {
console.log(this.#name)
}
// getter方法,用来读取属性
getName(){
return this.#name
}
// setter方法,用来设置属性
setName(name){
this.#name = name
}
getAge(){
return this.#age
}
setAge(age){
if(age >= 0){
this.#age = age
}
}
get gender(){
return this.#gender
}
set gender(gender){
this.#gender = gender
}
}
8.6 多态
多态
- 在JS中不会检查参数的类型,所以这就意味着任何数据都可以作为参数传递
- 要调用某个函数,无需指定的类型,只要对象满足某些条件即可
- 如果一个东西走路像鸭子,叫起来像鸭子,那么它就是鸭子
- 多态为我们提供了灵活性
8.7 继承
继承
-
可以通过extends关键来完成继承
-
当一个类继承另一个类时,就相当于将另一个类中的代码复制到了当前类中(简单理解)
-
继承发生时,被继承的类称为 父类(超类),继承的类称为 子类
-
通过继承可以减少重复的代码,并且可以在不修改一个类的前提对其进行扩展
封装 —— 安全性
继承 —— 扩展性
多态 —— 灵活性 -
通过继承可以在不修改一个类的情况下对其进行扩展
-
OCP 开闭原则
- 程序应该对修改关闭,对扩展开放
8.8 对象的结构
对象中存储属性的区域实际有两个:
1. 对象自身
- 直接通过对象所添加的属性,位于对象自身中
- 在类中通过 x = y 的形式添加的属性,位于对象自身中
- 原型对象(prototype)
-
对象中还有一些内容,会存储到其他的对象里(原型对象)
-
在对象中会有一个属性用来存储原型对象,这个属性叫做__proto__
-
原型对象也负责为对象存储属性,
当我们访问对象中的属性时,会优先访问对象自身的属性,
对象自身不包含该属性时,才会去原型对象中寻找
3.会添加到原型对象中的情况:
-
在类中通过xxx(){}方式添加的方法,位于原型中
-
主动向原型中添加的属性或方法
8.9 原型对象
8.9.1 什么是原型对象
所有 JavaScript 对象都从原型继承属性和方法。
日期对象继承自 Date.prototype。数组对象继承自 Array.prototype。Person 对象继承自 Person.prototype。
Object.prototype 位于原型继承链的顶端:
日期对象、数组对象和 Person 对象都继承自 Object.prototype。
- 显式原型对象
原型对象就是函数对象的一个属性prototype的值( 地址),这个prototype属性值是原型对象, 也被叫做显式原型对象.ES5中适合在原型内部添加东西—修改。
console.dir(Villa);
- 隐式原型对象
由这个函数实例化出来的对象身上都会有一个属性叫_proto_, 它和函数对象prototype地址一样, 代表同一个对象,如果我们通过__proto__去操作原型对象, 称为隐式原型对象。ES5中适合查看原型。
####8.9.2 原型的作用
原型就相当于是一个公共的区域,可以被所有该类实例访问,可以将该类实例中,所有的公共属性(方法)统一存储到原型中 , 这样我们只需要创建一个属性,即可被所有实例访问
JS中继承就是通过原型来实现的 , 当继承时,子类的原型就是一个父类的实例
在对象中有些值是对象独有的,像属性(name,age,gender)每个对象都应该有自己值,但是有些值对于每个对象来说都是一样的,像各种方法,对于一样的值没必要重复的创建
作用域链,是找变量的链,找不到会报错
原型链,是找属性的链,找不到会返回undefined
原型链就是对象在找属性或者方法的过程 我们一般都会把一些共有的属性或者方法添加到一个对象的原型上面 这样可以形成资源共享节省空间!
**所有的同类型对象它们的原型对象都是同一个,**也就意味着,同类型对象的原型链是一样的
8.9.3 修改原型
大部分情况下,我们是不需要修改原型对象
注意:千万不要通过类的实例去修改原型
-
通过一个对象影响所有同类对象,这么做不合适
-
修改原型先得创建实例,麻烦
-
危险
除了通过__proto__能访问对象的原型外,还可以通过类的prototype属性,来访问实例的原型
修改原型时,最好通过通过类去修改
好处:-
一修改就是修改所有实例的原型
-
无需创建实例即可完成对类的修改
-
原型尽量不要手动改
-
要改也不要通过实例对象去改
-
通过 类.prototype 属性去修改
-
最好不要直接给prototype去赋值
-
class Person {
name = "孙悟空"
age = 18
sayHello() {
console.log("Hello,我是", this.name)
}
}
Person.prototype.fly = () => {
console.log("我在飞!")
}
Array.prototype.sum = function() {
let result = this.reduce((pre,current) => pre + current);
console.log(result);
// let sum = 0;
// for (let i = 0; i < this.length; i++) {
// sum += this[i];
// }
// console.log(sum);
}
8.9.4 原型链
描述的是对象在查找属性或者方法的过程
实例化对象在找属性或者方法的时候,先从自身去找看有没有这个属性或者方法,如果有,直接使用这个属性的值或者方法,如果没有,会继续顺着这个对象的隐式原型对象(_proto_)找到这个对象的原型对象(和它的构造函数的显式原型对象是同一个),看看原型对象是否存在这个属性,如果有就使用原型对象当中的这个属性值,如果还没有,再去找原型对象的隐式原型对象(默认就是Object显式原型对象),找到以后去看看有没有这个属性,如果有就使用这个属性值;如果没有就返回undefined(代表已经找到顶了);
注意:
原型对象也有原型,这样就构成了一条原型链,根据对象的复杂程度不同,原型链的长度也不同
obj对象的原型链:obj对象 --> 原型 --> null
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zmpUUyop-1665326920516)(C:\Users\24601\AppData\Roaming\Typora\typora-user-images\image-20221007092837844.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VfpdVpGD-1665326920516)(C:\Users\24601\AppData\Roaming\Typora\typora-user-images\image-20221004140148403.png)]
总结
如果在函数对象上找不到 属性 或者 方法
就去原型对象上去找 属性和方法 找到就使用 找不到
就去Object的原型对象上去找属性或者方法
如果都找不到就返回undefined 如果是方法 那么就会报错
8.10 instanceof和hasOwn
instanceof 用来检查一个对象是否是一个类的实例
- instanceof检查的是对象的原型链上是否有该类实例
只要原型链上有该类实例,就会返回true
dog -> Animal的实例 -> Object实例 -> Object原型
- Object是所有对象的原型,所以任何和对象和Object进行instanceof运算都会返回true
in
使用in运算符检查属性时,无论属性在对象自身还是在原型中,都会返回true
对象.hasOwnProperty(属性名) (不推荐使用) 如果使用Object.creat(null) 生成一个空对象中没有方法,就无法使用
用来检查一个对象的自身是否含有某个属性
Object.hasOwn(对象, 属性名) (推荐使用)
用来检查一个对象的自身是否含有某个属性
8.11 旧类
早期JS中,直接通过函数来定义类
- 一个函数如果直接调用 xxx() 那么这个函数就是一个普通函数
- 一个函数如果通过new调用 new xxx() 那么这个函数就是一个构造函数
等价于 class Person {}
var Person = (function () {
function Person(name, age) {
// 在构造函数中,this表示新建的
this.name = name
this.age = age
// this.sayHello = function(){
// console.log(this.name)
// }
}
// 向原型中添加属性(方法)
Person.prototype.sayHello = functio
console.log(this.name)
}
// 静态属性
Person.staticProperty = "xxx"
// 静态方法
Person.staticMethod = function () {
return Person
})()
const p = new Person("孙悟空", 18)
// console.log(p)
关于继承的原理 子类的原型是父类的实例
var Animal = (function(){
function Animal(){
}
return Animal
})()
var Cat = (function(){
function Cat(){
}
// 继承Animal
Cat.prototype = new Animal() // 继承的原理,子类的原型等于父类的实例
return Cat
})()
var cat = new Cat()
console.log(cat)
8.12 new关键字
使用new时,到底发生了哪些事情:
当使用new去调用一个函数时,这个函数将会作为构造函数调用,
使用new调用函数时,将会发生这些事:
- 创建一个普通的JS对象(Object对象 {}), 为了方便,称其为新对象
- 将构造函数的prototype属性设置为新对象的原型
- 使用实参来执行构造函数,并且将新对象设置为函数中的this
- 如果构造函数返回的是一个非原始值,则该值会作为new运算的返回值返回(千万不要这么做)
如果构造函数的返回值是一个原始值或者没有指定返回值,则新的对象将会作为返回值返回
通常不会为构造函数指定返回值
function MyClass(){
// var newInstance = {}
// newInstance.__proto__ = MyClass.prototype
}
8.13 浅拷贝与深拷贝
拷贝(shallow copy)
- 通常对对象的拷贝都是浅拷贝
- 浅拷贝顾名思义,只对对象的浅层进行复制(只复制一层)
- 如果对象中存储的数据是原始值,那么拷贝的深浅是不重要
- 浅拷贝只会对对象本身进行复制,不会复制对象中的属性(或元素)
slice() 通常用来对数组进行浅拷贝 // **数组方法 **
深拷贝通过structuredClone( ) ,来实现 // 对象数组都可以使用
Object.assign (target, …sources) target:目标 sources:拷贝的对象 // 对象数组都可以使用
数组实现
let arr = [1, 2, 3, 4, 5];
// 1
let array = arr.slice();
// 2
let arr1 = structuredClone(arr);
// 3
let arr2 = [];
Object.assign(arr2, arr);
// 4
let arr3 = [...arr];
对象实现
let arr = [
{ name: "ling", age: 16 },
{ name: "li", age: 16 },
];
let arr1 = [...arr];
let arr2 = [];
Object.assign(arr2, arr); // 浅拷贝
let arr3 = structuredClone(arr); // 深拷贝
// 利用JSON来完成深复制
const str = JSON.stringify(obj)
const obj4 = JSON.parse(str)
8.14 call和apply方法
8.14.1 概念
任何函数对象都有apply和call方法
apply和call可以使用第一个参数传对象,让函数或者方法的执行者(this)指向这个对象;
apply和call方法中的this代表是方法中的第一个参数对象。
8.14.2 语法
call和apply可以让一个对象执行另外一个对象的方法;
函数或者方法.apply(对象,[函数的参数]); // 参数是数组
函数或者方法.call(对象,函数的参数1,函数的参数2); // 参数是字符串
8.14.3 作用
call和apply做了两件事:
1、调用的时候先把this指向改为你指定的第一个参数对象
2、然后再去自动执行使用的方法
8.14.4 案例
- 没有参数
var obj = {
name:'马坚强',
age:33
}
function Dog(name,age){
this.name = name;
this.age = age;
}
Dog.prototype.eat = function(){
console.log('吃肉');
}
var d1 = new Dog('旺财',3);
d1.eat();
obj.eat(); // 思考报错原因
-
解决策略
-
给这个对象自己添加一个吃肉的方法 obj.eat = function(){ console.log('吃肉'); } obj.eat();
-
不给这个对象添加,而是使用apply或者call借用 狗的吃肉方法去实现 d1.eat.apply(obj); //把狗的eat方法调用时的this指向修改为obj,代表obj在执行狗的这个方法 d1.eat.call(obj);
-
-
有参数
Dog.prototype.eat = function(a,b){ console.log(this); console.log('吃肉'); console.log(a,b); } var d1 = new Dog('旺财',3); d1.eat(10,20); d1.eat.apply(obj,[100,200]); d1.eat.call(obj,100,200);
8.15 总结
面向对象本质就是,编写代码时所有的操作都是通过对象来进行的。
面向对象的编程的步骤:
1. 找对象
2. 搞对象
学习对象:
1. 明确这个对象代表什么,有什么用
2. 如何获取到这个对象
3. 如何使用这个对象(对象中的属性和方法)
对象的分类:
内建对象
- 由ES标准所定义的对象
- 比如 Object Function String Number ....
宿主对象
- 由浏览器提供的对象
- BOM、DOM
自定义对象
- 由开发人员自己创建的对象
9. 内建对象
9.1 内置数学Math对象
9.1.1 round 四舍五入
// round四舍五入
console.log(Math.round(1.5)); // 2
console.log(Math.round(1.4)); // 1
console.log(Math.round(1.6)); // 2
console.log(Math.round(-1.4)); // -1
console.log(Math.round(-1.6)); // -2
// .5之外 其余的正常,不进位
0
20nsole.log(Math.round(-1.5)); // -1
9.1.2 floor 向下取整
console.log(Math.floor(1.4)); // 1
console.log(Math.floor(1.5)); // 1
console.log(Math.floor(1.6)); // 1
console.log(Math.floor(-1.0)); // -1
console.log(Math.floor(-1.1)); // -2
console.log(Math.floor(-1.5)); // -2
console.log(Math.floor(-1.6)); // -2
9.1.3 ceil 向上取整
console.log(Math.ceil(1.3)); // 2
console.log(Math.ceil(1.5)); // 2
console.log(Math.ceil(1.6)); // 2
console.log(Math.ceil(-1.3)); // -1
console.log(Math.ceil(-1.5)); // -1
console.log(Math.ceil(-1.6)); // -1
9.1.4 max 最大值
console.log(Math.max(9,1,2,3,4,5,8));
9.1.5 min 最小值
console.log(Math.min(9,1,2,3,4,5,8));
9.1.6 PI ( Π )
console.log(Math.PI);
9.1.7 pow 幂
console.log(Math.pow(2,4)); // 16
9.1.8 abs 绝对值
console.log(Math.abs(-5));
9.1.9 sqrt 开方
console.log(Math.sqrt(169));
9.1.10 random
// 随机的范围是0~1 左闭右开
console.log(Math.random());
// 我想拿到0 - 84之间的随机整数
console.log(Math.floor(Math.random() * 85));
// 我想拿到1 - 100之间的随机整数
console.log(Math.floor(Math.random() * 100 + 1));
// 我想拿到5 - 66之间的随机整数
console.log(Math.floor(Math.random() * 62 + 5));
// 任意两个数a,b之间的随机整数的计算规则
Math.floor(Math.random() * (b-a+1) + a);
9.2 Date对象
Date 在JS中所有的和时间相关的数据都由Date对象来表示
对象的方法: 需要用Date实例化的对象调用( new Date() )
getFullYear() 获取4位年份
getMonth() 返当前日期的月份(0-11)
getDate() 返回当前是几日
getHours() 返回的是小时
getMinutes() 返回的是分钟
getSeconds() 返回的秒
getDay() 返回当前日期是周几(0-6) 0表示周日
getTime() 返回当前日期对象的时间戳
时间戳:自1970年1月1日0时0分0秒到当前时间所经历的毫秒数
计算机底层存储时间时,使用都是时间戳
toLocaleDateString() 将日期转换为本地的字符串
toLocaleTimeString() 将时间转换为本地的字符串
Date.now() 获取当前的时间戳
9.2.1 toLocaleString
toLocaleString()
- 可以将一个日期转换为本地时间格式的字符串
- 参数:
1. 描述语言和国家信息的字符串
zh-CN 中文中国
zh-HK 中文香港
en-US 英文美国
2. 需要一个对象作为参数,在对象中可以通过对象的属性来对日期的格式进行配置
dateStyle 日期的风格
timeStyle 时间的风格
full
long
medium
short
hour12 是否采用12小时值
true
false
weekday 星期的显示方式
long
short
narrow
year
numeric
2-digit
result = d.toLocaleString("zh-CN", {
year: "numeric",
month: "long",
day: "2-digit",
weekday: "short",
})
9.2.2 案例-获取年月日
let div = document.querySelector("div");
function getDay() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth();
let day = date.getDate();
let time = date.toLocaleTimeString();
let result = year + "年" + month + "月" + day + "日 " + time;
div.innerHTML = result;
}
setInterval(() => getDay(), 1000);
9.3 包装类
在JS中,除了直接创建原始值外,也可以创建原始值的对象
- 通过 new String() 可以创建String类型的对象
- 通过 new Number() 可以创建Number类型的对象
- 通过 new Boolean() 可以创建Boolean类型的对象
但是千万不要这么做
包装类:
JS中一共有5个包装类
-
String --> 字符串包装为String对象
-
Number --> 数值包装为Number对象
-
Boolean --> 布尔值包装为Boolean对象
-
BigInt --> 大整数包装为BigInt对象
-
Symbol --> 符号包装为Symbol对象
通过包装类可以将一个原始值包装为一个对象,
当我们对一个原始值调用方法或属性时,JS解释器会临时将原始值包装为对应的对象, 然后调用这个对象的属性或方法
由于原始值会被临时转换为对应的对象,这就意味着对象中的方法都可以直接通过原始值来调用
9.4正则表达式
正则表达式 : 正则表达式用来定义一个规则
- 通过这个规则计算机可以检查一个字符串是否符合规则 或者将字符串中符合规则的内容提取出来
- 正则表达式也是JS中的一个对象, 所以要使用正则表达式,需要先创建正则表达式的对象
1.在正则表达式中大部分字符都可以直接写
2.| 在正则表达式中表示或
3.[] 表示或(字符集)
[a-z] 任意的小写字母
[A-Z] 任意的大写字母
[a-zA-Z] 任意的字母
[0-9]任意数字
4.[^] 表示除了
[^x] 除了x
5. . 表示除了换行外的任意字符
6. 在正则表达式中使用\作为转义字符
7. 其他的字符集
\w 任意的单词字符 [A-Za-z0-9_]
\W 除了单词字符 [^A-Za-z0-9_]
\d 任意数字 [0-9]
\D 除了数字 [^0-9]
\s 空格
\S 除了空格
\b 单词边界
\B 除了单词边界
8. 开头和结尾
^ 表示字符串的开头
$ 表示字符串的结尾
量词
{m} 正好m个
{m,} 至少m个
{m,n} m-n个
+ 一个以上,相当于{1,}
* 任意数量的a
? 0-1次 {0,1}
9.5 split
split 字符串转数组
var str = '1a2b3c4d5e6';
console.log(str.split(/[a-z]/));
9.6 search
search 找到第一个符合条件的下标
var str = 'kbc kec kfc kkc kac';
console.log(str.search(/k[fk]c/));
9.7 match
match 默认情况找到第一个就返回数据
var str = "1a2b3c4d5e6F";
// i 是忽略的意思 ignore
// g 是全局的意思 global
var result = str.match(/[a-z]/gi);
console.log(result); // abcdeF
9.8 matchAll
matchAll()
根据正则表达式去匹配字符串中符合要求的内容(必须设置g 全局匹配) 它返回的是一个迭代器 可以使用 for of 遍历
let str = "dajsdh13715678903jasdlakdkjg13457890657djashdjka13811678908sdadadasd";
let reg = /(1[3-9]\d)\d{4}(\d{4})/g;
let result;
result = str.matchAll(reg);
for (let key of result) {
console.log(key); // 13715678903 13457890657 13811678908
}
str.matchAll(regexp) regexp 如果所传参数不是一个正则表达式对象,则会隐式地使用 new RegExp(obj) 将其转换为一个 [
RegExp
]
RegExp
必须是设置了全局模式g
的形式,否则会报错。
###9.9 replace
replace 默认只是替换第一个符合条件的元素
var str = '1a2b3c4d';
console.log(str.replace(/[a-z]/g,'@_@')); // 1@_@2@_@3@_@4@_@
###9.10 exec()
exec ( ) 获取字符串中符合正则表达式的内容 返回一个结果数组或
null
let reg = /1[3-9]\d{9}/g;
reg = /(1[3-9]\d)\d{4}(\d{4})/g; // 用小括号分组,得到数组第一项就是匹配结果,第二项之后就是每一组的匹配结果
let str = "dajsdh13715678903jasdlakdkjg13457890657djashdjka13811678908sdadadasd";
let result;
while (result = reg.exec(str)) {
// console.log(result[0], result[1], result[2]);
console.log(result[1]+"****"+result[2]); // 137****8903 134****0657 138****8908
}
-----------------------------------------------------------------------------------------------------------------------------
reg = /^1[3-9]\d{9}$/;
console.log(reg.test("13456789042")); // true
如果匹配失败,
exec()
方法返回null
,并将正则表达式的lastIndex
重置为 0。如果匹配成功,
exec()
方法返回一个数组,并更新正则表达式对象的lastIndex
属性。完全匹配成功的文本将作为返回数组的第一项,从第二项起,后续每项都对应一个匹配的捕获组。在设置了
global
或sticky
标志位的情况下(如/foo/g
或/foo/y
),JavaScriptRegExp
对象是有状态的。它们会将上次成功匹配后的位置记录在lastIndex
属性中。使用此特性,exec()
可用来对单个字符串中的多次匹配结果进行逐条的遍历(包括捕获到的匹配),而相比之下,String.prototype.match()
只会返回匹配到的结果。 最重要的特点
9.11 垃圾回收机制
垃圾回收(Garbage collection)
- 和生活一样,生活时间长了以后会产生生活垃圾
程序运行一段时间后也会产生垃圾 - 在程序的世界中,什么是垃圾?
- 如果一个对象没有任何的变量对其进行引用,那么这个对象就是一个垃圾
- 垃圾对象的存在,会严重的影响程序的性能
- 在JS中有自动的垃圾回收机制,这些垃圾对象会被解释器自动回收,我们无需手动处理
- 对于垃圾回收来说,我们唯一能做的事情就是将不再使用的变量设置为null
9.12 结构赋值
9.12.1 数组-结构赋值
数组结构
let arr = ["比企谷八潘", "雪之下雪乃", "由比滨结衣"];
let [a, b, c, d] = arr;
console.log(a, b, c, d); // "比企谷八潘" "雪之下雪乃" "由比滨结衣" undefined 因为 d 没有与之对应的,所以返回undefined
函数返回值可以之间结构
let arr = ["比企谷八潘", "雪之下雪乃", "由比滨结衣"];
function fn() {
return arr;
}
let [a, ...b] = fn();
console.log(a, b); // "比企谷八潘" Array(2) b返回一个数组,包含数组后两项
变量交换
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
二维数组
let array = [
[1, 2, 3],
[4, 5, 6],
];
let [[a, b, c], ...aee] = array;
console.log(a, b, c, aee); // 1 2 3 [4, 5, 6]
let arr = [1, 2, 3];
let [a, b, c, d = 10] = arr; 当 d 没有对应的值的时候,d 的值就为 10
console.log(a, b, c, d);
9.12.2 对象-结构
当时用的变量名与 key值相等时
const obj = { name: "孙悟空", age: 18, gender: "男" };
let { name, age, gender } = obj; // 解构对象 "孙悟空" 18 "男"
当时用的变量名与 key值不相等时
const obj = { name: "孙悟空", age: 18, gender: "男" };
let { name: a, age: b, gender: g } = obj; // 解构对象 (a b g 所表示的值为) "孙悟空" 18 "男"
9.13 JSON
Json是一种轻量级的数据交换标准。
json是一种数据格式;现在我们大多数都是通过json数据格式进行前后端数据交互的,json本质上是一个字符串,简称json串
前端往后台传数据的时候,要传json格式的数据json串
在前端json串的格式原形就是对象或者对象的数组;所以我们要先把数据存储为对象或者对象的数组,然后转化为json串进行传递
JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
eg:
var obj = {a: ‘Hello’, b: ‘World’}; //这是一个对象,注意键名也是可以使用引号包裹的
var json = ‘{“a”: “Hello”, “b”: “World”}’; //这是一个 JSON 字符串,本质是一个字符串
var a = '阿马';
var b = 100;
var c = '嘿嘿';
//第一步,把数据给封成一个对象
var obj = {
a: a,
b: b,
c: c
}
//第二步,把对象给变为json串,依赖JSON对象的方法
console.log(obj);
var result = JSON.stringify(obj); //把对象转化为json串
console.log(result);
//后端传递回来的数据,我们需要把这个json串转化为对象去操作
console.log(JSON.parse(result));
9.14 Map
-
Map用来存储键值对结构的数据(key-value)
-
Object中存储的数据就可以认为是一种键值对结构
Map和Object的主要区别:
- Object中的属性名只能是字符串或符号,如果传递了一个其他类型的属性名,
JS解释器会自动将其转换为字符串 - Map中任何类型的值都可以称为数据的key
- Object中的属性名只能是字符串或符号,如果传递了一个其他类型的属性名,
const map = new Map();
map.set("name", "孙悟空");
map.set(obj2, "呵呵");
map.set(NaN, "哈哈哈");
map.delete(NaN);
// map.clear();
console.log(map);
console.log(map.get("name"));
console.log(map.has("name"));
9.14.1 Map方法
map.size() 获取map中键值对的数量
map.set(key, value) 向map中添加键值对
map.get(key) 根据key获取值
map.delete(key) 删除指定数据
map.has(key) 检查map中是否包含指定键
map.clear() 删除全部的键值对
// 将map转换为数组
// const arr = Array.from(map) // [["name","孙悟空"],["age",18]];
// const arr = [...map];
// console.log(arr);
const map2 = new Map([
["name", "猪八戒"],
["age", 18],
[{}, () => {}],
])
// console.log(map2;)
// 遍历map
// for (const [key, value] of map) {
// // const [key, value] = entry;
// console.log(key, value);
// }
// map.forEach((key, value)=>{
// console.log(key, value);
// })
/*
map.keys() - 获取map的所有的key
map.values() - 获取map的所有的value
*/
for(const key of map.keys()){
console.log(key);
}
9.15Set
- Set用来创建一个集合
- 它的功能和数组类似,不同点在于Set中不能存储重复的数据
使用方式:
- new Set()
- new Set([…])
方法
- size 获取数量
- add() 添加元素
- has() 检查元素
- delete() 删除元素
9.12.1 数组-结构赋值
数组结构
let arr = ["比企谷八潘", "雪之下雪乃", "由比滨结衣"];
let [a, b, c, d] = arr;
console.log(a, b, c, d); // "比企谷八潘" "雪之下雪乃" "由比滨结衣" undefined 因为 d 没有与之对应的,所以返回undefined
函数返回值可以之间结构
let arr = ["比企谷八潘", "雪之下雪乃", "由比滨结衣"];
function fn() {
return arr;
}
let [a, ...b] = fn();
console.log(a, b); // "比企谷八潘" Array(2) b返回一个数组,包含数组后两项
变量交换
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
二维数组
let array = [
[1, 2, 3],
[4, 5, 6],
];
let [[a, b, c], ...aee] = array;
console.log(a, b, c, aee); // 1 2 3 [4, 5, 6]
let arr = [1, 2, 3];
let [a, b, c, d = 10] = arr; 当 d 没有对应的值的时候,d 的值就为 10
console.log(a, b, c, d);
9.12.2 对象-结构
当时用的变量名与 key值相等时
const obj = { name: "孙悟空", age: 18, gender: "男" };
let { name, age, gender } = obj; // 解构对象 "孙悟空" 18 "男"
当时用的变量名与 key值不相等时
const obj = { name: "孙悟空", age: 18, gender: "男" };
let { name: a, age: b, gender: g } = obj; // 解构对象 (a b g 所表示的值为) "孙悟空" 18 "男"
9.13 JSON
Json是一种轻量级的数据交换标准。
json是一种数据格式;现在我们大多数都是通过json数据格式进行前后端数据交互的,json本质上是一个字符串,简称json串
前端往后台传数据的时候,要传json格式的数据json串
在前端json串的格式原形就是对象或者对象的数组;所以我们要先把数据存储为对象或者对象的数组,然后转化为json串进行传递
JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
eg:
var obj = {a: ‘Hello’, b: ‘World’}; //这是一个对象,注意键名也是可以使用引号包裹的
var json = ‘{“a”: “Hello”, “b”: “World”}’; //这是一个 JSON 字符串,本质是一个字符串
var a = '阿马';
var b = 100;
var c = '嘿嘿';
//第一步,把数据给封成一个对象
var obj = {
a: a,
b: b,
c: c
}
//第二步,把对象给变为json串,依赖JSON对象的方法
console.log(obj);
var result = JSON.stringify(obj); //把对象转化为json串
console.log(result);
//后端传递回来的数据,我们需要把这个json串转化为对象去操作
console.log(JSON.parse(result));
9.14 Map
-
Map用来存储键值对结构的数据(key-value)
-
Object中存储的数据就可以认为是一种键值对结构
Map和Object的主要区别:
- Object中的属性名只能是字符串或符号,如果传递了一个其他类型的属性名,
JS解释器会自动将其转换为字符串 - Map中任何类型的值都可以称为数据的key
- Object中的属性名只能是字符串或符号,如果传递了一个其他类型的属性名,
const map = new Map();
map.set("name", "孙悟空");
map.set(obj2, "呵呵");
map.set(NaN, "哈哈哈");
map.delete(NaN);
// map.clear();
console.log(map);
console.log(map.get("name"));
console.log(map.has("name"));
9.14.1 Map方法
map.size() 获取map中键值对的数量
map.set(key, value) 向map中添加键值对
map.get(key) 根据key获取值
map.delete(key) 删除指定数据
map.has(key) 检查map中是否包含指定键
map.clear() 删除全部的键值对
// 将map转换为数组
// const arr = Array.from(map) // [["name","孙悟空"],["age",18]];
// const arr = [...map];
// console.log(arr);
const map2 = new Map([
["name", "猪八戒"],
["age", 18],
[{}, () => {}],
])
// console.log(map2;)
// 遍历map
// for (const [key, value] of map) {
// // const [key, value] = entry;
// console.log(key, value);
// }
// map.forEach((key, value)=>{
// console.log(key, value);
// })
/*
map.keys() - 获取map的所有的key
map.values() - 获取map的所有的value
*/
for(const key of map.keys()){
console.log(key);
}
9.15Set
- Set用来创建一个集合
- 它的功能和数组类似,不同点在于Set中不能存储重复的数据
使用方式:
- new Set()
- new Set([…])
方法
- size 获取数量
- add() 添加元素
- has() 检查元素
- delete() 删除元素