学习Javascript这一篇就足够

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

  1. ‘’ // 空字符串

  2. 0

  3. NaN

  4. null

  5. false

  6. 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 接口,比如ArrayMapSet、**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:数组中正在处理的当前元素的索引。

arrayforEach() 方法正在操作的数组。

不能使用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() 方法返回一个新的数组对象,这一对象是一个由 startend 决定的原数组的浅拷贝(包括 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() 方法返回一个新的数组对象,这一对象是一个由 beginend 决定的原数组的浅拷贝(包括 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 函数的声明方式

  1. function fun()
  2. let fn = function() {}
  3. 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和原型argumentsthis指向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. 程序是干嘛的?

  • 程序就是对现实世界的抽象(照片就是对人的抽象)

对象是干嘛的?

  • 一个事物抽象到程序中后就变成了对象

  • 在程序的世界中,一切皆对象

    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 封装

面向对象的特点:
封装、继承和多态

  1. 封装
  • 对象就是一个用来存储不同属性的容器
  • 对象不仅存储属性,还要负责数据的安全
  • 直接添加到对象中的属性,并不安全,因为它们可以被任意的修改
    • 如何确保数据的安全:
  1. 私有化数据

    • 将需要保护的数据设置为私有,只能在类内部使用
      2.提供setter和getter方法来开放对数据的操作

    • 属性设置私有,通过getter setter方法操作属性带来的好处

      1. 可以控制属性的读写权限
      2. 可以在方法中对属性的值进行验证
    • 封装主要用来保证数据的安全

    • 实现封装的方式:
      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 的形式添加的属性,位于对象自身中
  1. 原型对象(prototype)
  • 对象中还有一些内容,会存储到其他的对象里(原型对象)

  • 在对象中会有一个属性用来存储原型对象,这个属性叫做__proto__

  • 原型对象也负责为对象存储属性,
    当我们访问对象中的属性时,会优先访问对象自身的属性,
    对象自身不包含该属性时,才会去原型对象中寻找

​ 3.会添加到原型对象中的情况:

  • 在类中通过xxx(){}方式添加的方法,位于原型中

  • 主动向原型中添加的属性或方法

8.9 原型对象

8.9.1 什么是原型对象

所有 JavaScript 对象都从原型继承属性和方法。

日期对象继承自 Date.prototype。数组对象继承自 Array.prototype。Person 对象继承自 Person.prototype。

Object.prototype 位于原型继承链的顶端:

日期对象、数组对象和 Person 对象都继承自 Object.prototype。

  1. 显式原型对象

原型对象就是函数对象的一个属性prototype的值( 地址),这个prototype属性值是原型对象, 也被叫做显式原型对象.ES5中适合在原型内部添加东西—修改。

console.dir(Villa);

  1. 隐式原型对象

由这个函数实例化出来的对象身上都会有一个属性叫_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去调用一个函数时,这个函数将会作为构造函数调用,
​ 使用new调用函数时,将会发生这些事:

  1. 创建一个普通的JS对象(Object对象 {}), 为了方便,称其为新对象
  2. 将构造函数的prototype属性设置为新对象的原型
  3. 使用实参来执行构造函数,并且将新对象设置为函数中的this
  4. 如果构造函数返回的是一个非原始值,则该值会作为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-11getDate()            返回当前是几日
 getHours()           返回的是小时
 getMinutes()         返回的是分钟
 getSeconds()         返回的秒
 getDay()             返回当前日期是周几(0-60表示周日
 getTime()            返回当前日期对象的时间戳
                      时间戳:自197011000秒到当前时间所经历的毫秒数
                      计算机底层存储时间时,使用都是时间戳
 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 属性。完全匹配成功的文本将作为返回数组的第一项,从第二项起,后续每项都对应一个匹配的捕获组。

在设置了 globalsticky 标志位的情况下(如 /foo/g/foo/y),JavaScript RegExp 对象是有状态的。它们会将上次成功匹配后的位置记录在 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
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
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() 删除元素
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
有不少的同学发信给我,和我探讨java的自学过程应该是什么样的,毕竟有很多人因为各种各样的原因不能参加培训。我试着给出自己的见解,抛砖引玉吧。 这个路线图是给那些为了就业的人准备的,如果只是兴趣,不一定照这个走。 这里只是讲了路线图,关于路线中的各个阶段,学到什么程度,如何学习等,可以参考后面的JAVA自学之路 七:《路线图明细》。 首先要学JavaSE,这是无庸置疑的。 与此同时,是的,与此同时,和JavaSE的学习同步,建议大家研究一下数据结构与算法。 在这儿之后,你可以选择向J2ME、或者Java GUI、Socket编程等方向努力,但是通过第一篇的办法,你可以很容易就将这方向过滤掉(永远不要忘了我们是为了就业),好吧,暂时和它们诀别,还是向着J2EE的方向前进吧。 在JavaSE完成之后,可以试着完成一些小项目,同时关注一下设计模式的内容,不必强求自己能够完全掌握各种细节,往前走吧。 掌握一种编程工具,比如说Eclipse。 数据库,可以选择使用Oracle或者MySQL开始 。 JDBC,用Java连接数据库。 可以选择在这个地方切入Hibernate,也可以选择在Struts 之后。 HTML CSS JavaScript,这些东西是做web项目必需的内容 。 Servlet JSP,服务器端必需。 XML AJAX,新的流行趋势不能不了解。 作一些小项目吧。比如BBS、留言系统、学籍管理、商城等等,随便着一些业务逻辑比较简单的来做一做。 Struts,MVC的典型应用 Hibernate,OR Mapping的典范之作。 Spring IOC + AOP, 面向对象 + 面向切面,完善的旅程。 把以前自己的小项目加入Struts、Spring、Hibernate的元素吧。 去研究各种商业项目的业务逻辑吧,才能在你的简历中加入浓重的一笔。 EJB3.0,java在走了很多弯路后的回归之作,值得研究。 SOA,业界铁定的未来趋势,要紧紧跟随。 在后面,我会告诉你每一步的学习方法和学习重点。 JAVA自学之路 三:要动手 无论如何,请坚持不懈的动手实验! 学习Java要动手,仅仅是看和听是不能够学好编程的。总有同学来信问我们这样的问题,说:"老师我看了视频了,可是怎么一动手就没有任何思路呢?" 对此,我一般都会说不要着急,如果你只是看了视频就想行云流水的话,那老师这些年的锻炼真是白费了,这么多年的苦练才能略有些行云流水的感觉,你一朝一夕就想自己能够达到,不是太奢侈了吗 作为编程的过来人想一下,当年自己学编程的时候是什么时候有思路的呢?至少也是照猫画虎画了一段时间之后吧,我本人是在某一个领域画虎画了上万行代码的时候才慢慢有自己思路的。就像写作文,要有自己思路总是一般来说要经过几个阶段,首先学习词汇,然后学习造句,然后大量阅读别人的文章,自己模仿着写一些,逐渐的经验积累,才能形成自己的思路。学编程,恐怕也得慢慢的来吧,只是看一看、听一听视频,是不足以学好编程的。 所以,动手吧,跟着做,一行一行的跟着敲,一个项目敲完了,比如坦克,再试着加一些自己的功能,按照自己的思路敲一些代码,收获远比干听大的多。 如果遇到,暂时对于一些思路不能理解的同学,我一般都会劝告说,动手写,先写代码,量变会引起质变的。而这种质变的确发生过不少次。 提醒一句,要理解代码思路之后再跟着敲 ,背着敲,千万不要左边摆着别人的程序,右边自己一个一个子母的写,这就不再是程序员了,成了打字员了。 纸上得来终觉浅,所以:别问那么多,别想那么多,动手写吧。 JAVA自学之路 四:要事为先 对待人生的任何事情都要:抓大放小,要事为先 对于一个以前从来没有接触过java的人,java无疑是庞大的,似乎每个领域都可以拓展开来都是一片开阔地,每一个领域要想深入接触到每一个细节所耗费的精力都是巨大的。这个时候大家都胸怀壮志,两眼发光的盯着每一个崭新的知识点,遇见了任何一个知识点都恨不得抠的清清楚楚,明明白白。 难道这有什么不对吗? 我的意见是,没什么大毛病,但是学习效率太低了!我们推荐的学习方式是,抓大放小,要事为先。 比如说学习J2SE的时候,学到GUI这一章,有的同学认为,既然学到这儿了,我们去把SWING细细的了解一遍吧;还有的同学,遇到了在Linux下的Shell编程,就恨不得把Shell先学成专家才肯,或者是才敢,继续往下学习;有没有过这样的感觉,一本书的上册没有学好,觉着下册心里也没底儿,甚至非要把上册复习一遍才敢继续学习。如果你也是这样,要去看看心理医生啦,没准儿会有洁癖的毛病。 任何事情都要追求完美才敢继续往后进行,是一种性格缺陷 大胆的放弃一些东西吧,有失才有得,把自己有限的、宝贵的精力用在对于就业直接相关的地方,这才是最有效率的学习方式!等你参加工作,有了可持续发展的事业动力和经济基础,有时间有精力闲的没事儿干的时候,再去研究那些其它吧。 曾经有一个故事,说得是产能和产量的关系。 一个人喜欢读书,读了一辈子,无所不通,这时候要做任何工作都可以,他的产能到了极致,但是,他老了,即便每天产出大量的东西也不能维持多久了,一辈子的产量并没有多少。 另一个人直接工作,忙,天天在机器旁日复一日,做了一辈子,没时间学习和进修,可以想象,产量能有多大呢。 人的一生,要锻炼产能,也要及时产出,引擎要转,也要停下来加油,这两者平衡的越好,你的贡献和产量就会越大。如果钻研的东西和产能目标(或者说近期的产能目标)无关的话,要舍得放手。 所以,对于SWING,暂时先扔在一边吧。 对于shell,对于各种协议过于详细的细节,对于喜欢的游戏编程,对于javascript各种华而不实的效果,都暂时和它们分别吧。一切和我们的直接工作目标关联不大的东西,扔在一边或者弄清楚到足够支持下一步的学习就可以了。 那这样岂不是妨碍我成为通晓各种细节的高手了吗? 我一向不认为一个人掌握了很多很多知识点的细节就是高手了,一个人如果告诉你,回字有四种写法,你会认为他是高手吗? 毫不客气的说,现在我们所教授的所有知识细节在网上或书中都可以找到,只要你肯花时间花精力去动手试,去钻研,没有什么不能掌握的。struts、spring你能掌握吗?当然能!但是如果把时间花在spring的各种细节的研究上,花在研究中国企业应用不广泛地的spring mvc上,这个精力就太浪费了,而且学习的积极性会受到打击,谁愿意整天泡在细节的蜘蛛网中挣扎不出来呢?谁愿意天天经历风雨却总是不能见到彩虹呢? 盖房子,要先建骨架,再谈修饰。 画山水,要先画结构,再谈润色。 认识一台结构复杂的机器,应该首先认清楚脉络,然后再逐步认识每一个关节。 为了应付从小学到大学的考试,我们背了各种各样的不应该提前掌握的细节,同时也养成了见到细节就抠的学习习惯。而现在,是到改改的时候了。 请在合适的时间,做合适的事情吧。 把时间和精力花在作项目上面,花在写作品以及锻炼解决问题的能力上面吧,这是迈向高手的正确的而且快速的方向。 我一直不认为一个课程提供了很多很多的细节就是优秀的价值高的课程了,掌握必要的细节,这个不含糊,至于其他,或者通过视频给出(这样可以给出更多的细节,但是不占上课时间,课上只讲重要的、必要的细节知识),或者在掌握了自学的能力后自己能够查出,这才是正途。 当你看书到某个地方暂时不理解的时候的,暂时放手吧,追求一些行云流水、自然而然的境界吧,只是不要停下前进的脚步,不要被大路旁边的支根末节干扰了你前进的行程,项目,真实的项目,这才是目的,就在不远的前方。 陶渊明读书,叫做“不求甚解”,但他是大诗人。 诸葛亮读书,总是“观其大略”,但他是大政治家。 作研究的,是在确定了方向之后才详细的研究深入的东西。 到了庐山,脉络形状扔在一边,盯着一棵棵小草、一粒粒石子的看,怎么样才能识得“真面目”? 作为应用型的人才,是研究回字的n种写法还是抓紧时间向着主要应用的方向努力,请自己作出抉择。 以项目驱动自己的学习吧,当把握了技术的脉络之后再去补充细节的研究,是正途。 这辈子就不研究其他的方向了吗? 当然要研究!但是在将来合适的时间再说吧。 所以,抓大放小,要事为先! 那么,在JAVA的这些课程中,哪些才是大,才是要事呢?请继续读下去。 JAVA自学之路 五:问题解决之道 既然是学习,就不可能不遇到问题。 既然是自学,就没有方便的和老师探讨的机会。 那么,遇到问题之后,环境配不通,程序调不过,运行不正常,遇见这些恼人的问题的时候,该怎么办呢? 首先我要恭喜你,遇见问题,意味着你又有涨经验的机会了,每解决一个问题,你的JAVA经验值就应该上升几百点,问题遇到的越多,知识提升的越快。 但是总是解决不了也是很恼人的,怎么办呢? 我一般要求我们的学生都是这样来进行的。 当你遇到一个问题的时候: 1:要仔细的观察错误的现象,是的,要仔细 有不少同学的手非常快,在编译一个程序的时候,报了一大堆的错误,扫了一眼之后就开始盯着代码一行一行的找,看清什么错误了吗?没有!有的时候安装软件出问题了,一个对话框弹出来说出错了,马上举手问老师:“不得了了,出错了,出错了”。 “什么错误?” “还没看呢?” 这都是典型的不上心的方法!请记住,学习编程并不是一件很容易的事情,自己首先要重视,要用心才可以。 在开发中,仔细观察出错信息,或者运行不正常的信息,是你要做的第一件事。 读清楚了,才能在以后的步骤中有的放矢,哭了半天,总要知道哭的是谁才成。 这里又分三种情况: A:错误信息读懂了,那么请进入2步:要仔细思考问题会出在哪些环节 B:没读懂,愣是一点没看懂,进入第4步吧:google C:读了个半懂,有些眉目但是不太能确定,第2步和第4步结合着来。 2:要仔细思考问题会出在哪些环节(重要) 当你读懂了一个问题之后,要好好的思考这个问题可能会在哪些环节上出错。 一辆汽车从总成线上下来,车门子关不上! 哪错了?你怎么查? 当然是顺着生产线一站一站的查下来。 程序也是一样的,也是一系列语句完成后产生的结果。 写一个网络程序,总是发现服务器端打印不出接收的数据,有几个环节会出错? 仔细分析这个现象的环节: 客户端产生数据->按“发送”按钮->发送到服务器->服务器接收到后打印 这几个环节都有可能会出错: 有可能客户端根本就没产生数据,有可能发送按钮按下去后根本就没发出去,或者发出去的不是你产生的东西,或者根本就没连接网络,或者发送出去服务器没有接收到,或者接收到之前就打印了等等等等。 学着去这样仔细的分析程序的环节和这些环节可能会产生的问题,你的经验值定然会大幅度快速的提升,这样做很累人,但是一件事情如果做下来一点都不累的话,这个东西还有价值吗? 在网页A输入了一个人的名字,提交到B,首先存储到数据库,然后再读出来,发现乱码!怎么办?当然是分析环节: 客户输入->HTTP发送->B接收->存储到数据库->读出->展现到网页 每个环节都可能出问题,怎么才能知道哪里出的问题?继续往下读。 3:如何定位错误(重要)分析清楚有哪些环节之后,下一步就是定位到底什么环节出错了。 定位有以下三种办法: A 打印输出,比如java的System.out.println(),比如js的alert(),这种办法常用,必须掌握 B Debug,可以参考我们的视频《坦克大战》,详细讲了Eclipse的调试。 C 删掉一部分调试一部分,也就是去掉一部分的功能,做简化,然后调试剩下的功能,JSP和JavaScript常用。 4:如果还不行,google吧 还查不出来?恭喜你,你遇到的错误是值得认真对待的错误,是会影响你学习生涯的错误,问一下google或者百度吧。照着下面的方法查查看。 一般来讲,搜索引擎可以搜到各种知识点,我曾经跟同学们开玩笑说:“学会了怎么google或者baidu,其实没必要跟老师学了,自己全都搞懂了,尚学堂是没必要来的。”当然,只是玩笑,培训的作用有三个: A:系统化知识 B:真实项目锻炼经验 C:少走弯路,节省时间 不知不觉开始做广告了,请原谅,处其位谋其事,总得为尚学堂说点什么:) 言归正传,如何查搜索引擎? 先精后粗,首先先进行尽量精确的查找,比如一个错误,SocketException,你怀疑它是在connect()方法出的问题,那么当然是选这样的关键词java connect SocketException 先中后英,本着以解决问题为主的想法,练习英文还是先放在一边吧,首先应该在中文网页中查询,还不行的话,搜索英文的吧,最近的尚学堂课程中会带大家阅读英文的书籍。有很多东西就像一层窗户纸,远看灰蒙蒙怪唬人的,你壮着胆子一捅,它就破了。阅读英文的书籍就是如此,不是想象中的那么困难:)宁可在沙场上战死,也不能被吓死不是吗:) 信息筛选,搜索出来的结果不见得能够完全匹配,建议大家多阅读前几页的搜索结果,多打开几个网页看看,不过,我的经验是超过3页一般就没意义了,所以超过3页还没有找到合适的答案,或许应该调整一下关键词,或者放粗整个搜索的结果了。 经常的进行知识难点的查询,如果一个问题牵扯的面比较广,就干脆到网上搜索一些相关的专题,比如“java 乱码 mysql” “oracle 创建用户”等等,如果有必要,不要犯懒,勤动手写一些小小的测试程序,来弄明白知识点的细节。这也是涨知识的重要的途径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值