js 复习

 

js发展重要事件

 

 

 avaScript 的基本语法和对象体系,是模仿 Java 而设计的。但是,JavaScript 没有采用 Java 的静态类型。正是因为 JavaScript 与 Java 有很大的相似性

avaScript 语言的函数是一种独立的数据类型,以及采用基于原型对象(prototype)的继承链。这是它与 Java 语法最大的两点区别。JavaScript 语法要比 Java 自由得多

Java 语言需要编译,而 JavaScript 语言则是运行时由解释器直接执行。

 什么是JavaScript?

JavaScript 是一门弱类型的动态脚本语言,支持多种编程范式,包括面向对象和函数式编程,被广泛用于Web 开发。


JavaScript是一门基于原型的动态解释性脚本语言

JavaScript 组成

ECMAScript,描述了该语言的语法和基本对象
文档对象模型(DOM),描述处理网页内容的方法和接口
浏览器对象模型(BOM),描述与浏览器进行交互的方法和接口

BOM - 浏览器对象模型
一套操作浏览器功能的API
通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率

DOM - 文档对象模型
一套操作页面元素的API

DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作

 表达式    类似于 数学公式   加减乘除与或非

变量   可以理解为一个容器,用于存放各种各样的值。

命名规则

- 由字母、数字、下划线、$符号组成,不能以数字以及其他符号开头
- 不能是保留关键字,例如:for、while

// 声明变量 变量名为 num 没有赋值 默认值为 undefined undefined也是一个 JavaScript 关键字,表示“无定义”。
var num;
// 这一步 将 1 + 1 的结果赋值给 变量 num 让str所代表的的内存地址所存储的内容为 1 + 1 的值, 后续我们想要使用 1 + 1的结果 可以直接通过 调用变量 num 调用
num = 1 + 1; 

es6 增加  let  const

简单理解   var  是早期出现的有一些问题存在

let 声明变量可以不用赋值  要修改的变量都用它就好了

const  声明常量,且不能改变其本身

const a = 12   那么a的值就不能改,a=2  就不行

const a = []    {}     那么就不能a = []  {}   但是可以a.push()   a.cc = ''xx"

JS中注释

// 这是单行注释 ctrl+/      ctrl+/

/*                                      /** enter
这是
多行
注释
ctrl+shift+/
*/

 数据类型

数值(number):整数和小数(比如 1 和 3.14 )
字符串(string):文本(比如 'Hello World' )。
布尔值(boolean):表示真伪的两个特殊值,即 true (真)和 false (假)
undefined :表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值
null :表示空值,即此处的值为空 。
对象(object):各种值组成的集合。
symbol (symbol ) : 唯一标识符 //es6学习前不做讨论

基础类型 存储在 栈内存 中
Undefined、Null、Boolean、Number、String和symbol

 数值判断
NaN:not a number 表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合。
NaN 与任何值都不相等,包括他本身
isNaN: is not a number
NaN进行任何数学运算 结果也是 NaN

 字符串类型

JS隐式转换 

1. 两边只要有一个是字符串,那么+就是字符串拼接功能
2. 两边如果都是数字,那么就是算术功能

es6  有模板字符串  ``

按位取值(只读)

var str = 'hello world!';
console.log(str[1]); //e
str[1] = 'x'; //无法改写

 Boolean类型

Boolean字面量: true和false,区分大小写
计算机内部存储:true为1,false为0

Undefined和Null

 1. undefined表示一个声明了没有赋值的变量,变量只声明的时候值默认是undefined
2. null表示一个空,变量的值如果想为null,必须手动设置

在javascript中 null是 对象的延伸 是一个 '空' 对象。 var str = ''; 中  '' 不能用null判断 他是有值的 内存中分配了空间来存储  '' 一个空字符串值。 null 在javascript中常见于释放内存空间  var str = null;

 JavaScript 有三种方法,可以确定一个值到底是什么类型。

typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
typeof undefined //"undefined"
typeof null // "object

typeof 针对未声明的变量

if (typeof v === "undefined" ) {
console.log("变量 v 不存在")
}

 isNaN 方法可以用来判断一个值是否为 NaN 

isNaN(NaN) // true
isNaN(123) // false

但是, isNaN 只对数值有效,如果传入其他值,会被先转成数值。比如,传入字符串的时候,字符串会被先转成NaN ,所以最后返回 true ,这一点要特别引起注意。也就是说, isNaN 为 true 的值,有可能不是 NaN ,而是一个字符串

isNaN('Hello') // true
// 相当于
isNaN(Number('Hello')) // true

出于同样的原因,对于对象和数组, isNaN 也返回 true 。

isNaN({}) // true
// 等同于
isNaN(Number({})) // true
isNaN(['xzy']) // true
// 等同于
isNaN(Number(['xzy'])) // true

但是,对于空数组和只有一个数值成员的数组, isNaN 返回 false 。

isNaN([]) // false
isNaN([123]) // false
isNaN(['123']) // false

上面代码之所以返回 false ,原因是这些数组能被 Number 函数转成数值

因此,使用 isNaN 之前,最好判断一下数据类型。

function myIsNaN(value) {
return typeof value === 'number' && isNaN(value);
}

判断 NaN 更可靠的方法是,利用 NaN 为唯一不等于自身的值的这个特点,进行判断

function myIsNaN(value) {
return value !== value;
}

isFinite 方法返回一个布尔值,表示某个值是否为正常的数值

 Infinity 、 -Infinity 、 NaN 和 undefined 这几个值会返回 false , isFinite 对于其他的数值都会返回 true 。

 运算符

指数运算符: x ** y

余数运算符: x % y

前置++:先加1,后参与运算
后置++:先参与运算,后加1

let n = 2, m = 10

        let c = ++n;  // c=3

        let d = m++   // d=10

        console.log(n, m); // 3 11

        console.log('c=' + c, "c=" + d);

说明m++  当代码执行一次的时候值没有发生改变,等到第二次在+1

 逻辑运算符(布尔运算符)

&& 与 两个操作数同时为true,结果为true,否则都是false
|| 或 两个操作数有一个为true,结果为true,否则为false
! 非 取反

关系运算符(比较运算符

==与===的区别:==只进行值得比较,===类型和值同时相等,则相等 

 赋值运算符

= += -= *= /= %=

 运算符的优先级

优先级从高到底
1. () 优先级最高
2. 一元运算符 ++ -- !
3. 算数运算符 先* / % 后 + -
4. 关系运算符 > >= < <=
5. 相等运算符 == != === !==
6. 逻辑运算符 先&& 后||
7. 赋值运算符
8. 默认从左至右 除了 赋值运算 = 三目运算 ?: 指数运算 **

 数据类型转换

String

字符串的颜色是黑色的,数值类型是蓝色的,布尔类型也是蓝色的,undefined和null是灰色的

var num = 5;
console.log(num.toString())

String()函数存在的意义:有些值没有toString(),这个时候可以使用String()。比如:undefined和null

转换成数值类型

Number()可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回NaN

var str = '500';
console.log(+str); // 取正  500
console.log(-str); // 取负  -500 
console.log(str - 0);        隐式转换  500

转换成布尔类型

Boolean()

''(空字符串) null undefined NaN 0 会转换成false 其它都会转换成true

 隐式转换

1. 如果包含的是有效数字字符串或者是有效浮点数字符串,则会将字符串转换(Number())为数值,再进行加减操作,返回值的类型是:number类型。
2. 如果不包含有效数字字符串,则会将字符串的值转换为NaN,返回值的类型是:number类型。var num1 = parseInt("12.3abc");  // 返回12,如果第一个字符是数字会解析知道遇到非数字结束
var num2 = parseInt("abc123");  // 返回NaN,如果第一个字符不是数字或者符号就返回NaN
var str = '500';
console.log(+str); // 取正
console.log(-str); // 取负
console.log(str - 0);
3. 如果是boolean类型,则先会把true或者false转换为1或者0,再进行加减操作,返回值的类型是:number类型。
4. 如果是null类型,则先会把null转换为0,在进行加减操作,返回值的类型是:number类型。
5. 如果是undefined,则先会把undefined转换为NaN(非数字),再进行加减操作,返回值的类型是:number类型。
6. 如果是对象,则先会通过对象的valueOf()方法,进行转换,如果返回的是NaN,调用toString()方法,在进行前面的操作,返回值的类型是:number类型。(注:空数组[]会返回0,在进行加减操作,空对象则会返回NaN)

 逻辑操作符中的隐式转换规律

&& ||  用的最多

注:只有undefined、null、NaN、0、空字符串会被转换为false,其余都为true

注:逻辑操作符的运算为短路逻辑运算:前一个条件已经能够得出结果后续条件不再执行!)

 关系操作符的隐式转换规律

(关系操作符的操作值也可以是任意类型):
1. 如果两个操作值都是数值,则直接比较大小。
2. 如果两个操作值都是字符串,则字符串进行其Unicode编码进行比较。
3. 如果一个操作值是数值,则另一个值转换为数值进行比较。
4. 如果一个操作值是对象,则调用对象的valueOf()和toString()方法,然后再进行上述比较。
5. 如果一个操作值是布尔值,则将布尔值转换为数值再进行比较。
(注:NaN和任何值都不相等,包括自己,同时它与任何类型比较都会返回false。

 相等操作符==和===的隐式转换规律:

1. 布尔值、字符串和数值进行比较,会先将其转换为数值再进行比较。
2. null和undefined比较是相等的,但不是全等的。
3. NaN与任何值都不相等,都会返回false

 布尔类型的隐式转换

流程控制语句会把后面的值隐式转换成布尔类型

转换为true 非空字符串 非0数字 true 任何对象
转换成false 空字符串 0 false null undefined NaN

 转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:

如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false 转换为 0,而true 转换为 1;


如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为值;


如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法,用得到的基本类型值按照前面的规则进行比较

这两个操作符在进行比较时则要遵循下列规则 null undefined

null 和 undefined 是相等的。
要比较相等性之前,不能将 null 和 undefined 转换成其他任何值。
如果有一个操作数是 NaN,则相等操作符返回 false,而不相等操作符返回 true。

重要提示:即使两个操作数都是 NaN,相等操作符也返回 false;因为按照规则,

NaN 不等于 NaN。
表达式 值
null==undefined true
“NaN”==NaN false
5==NaN false
NaN==NaN false
false==0 true
true==1 true
true==2 false

undefined==0 false
null==0 false

如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,
则相等操作符返回 true;否则,返回 false。

null == undefined 会返回 true,因为它们是类似的值;但 null === undefined 会返回 false,因为它们是不同类型的值。

=== 转换   是es6中推荐的

关于 null 在关系运算和相等运算中的坑: 

null > 0 // null 尝试转型为number , 则为0 . 所以结果为 false,
null >= 0 // null 尝试转为number ,则为0 , 结果为 true.
null == 0 // null在设计上,在此处不尝试转型. 所以 结果为false.

1. 关系运算符 和 相等运算符 并不是一个类别的.
2. 关系运算符,在设计上,总是需要运算元尝试转为一个number . 而相等运算符在设计上,则没有这方面的考虑.
3. 最重要的一点, 不要把 拿 a > b , a == b 的结果 想当然的去和 a >= b 建立联系. 正确的符合最初设计思想的关
系是 a > b 与 a >= b是一组 . a == b 和其他相等运算符才是一组. 比如 a === b , a != b, a !== b .

 三元运算符

表达式1 ? 表达式2 : 表达式3

i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

 switch (expression) {
 case 常量1:
  语句;
  break;
 case 常量2:
  语句;
  break;

 交互与写入:alert、prompt 和 confirm / write

alert("Hello");    小心弹出框   确定  取消

pormpt有文本消息的模态窗口,还有 input 框和确定/取消按钮

var age = prompt('How old are you?', 100);
alert("You are" + age +"ars old!");

先弹出对话框  How old are you 

点击确认后弹出 you are 100 ars old!

点击取消后弹出 you are null ars old!

confirm  有 两个按钮的模态窗口

var isBoss = confirm("Are you the boss?");
alert( isBoss ); // 如果“确定”按钮被按下,则显示 true

document.write('我被写入了BODY中','还有我');

直接显示在网页上

 while语句

var i = 0;
while (i < 3) { // 依次显示 0、1 和 2
 console.log(i);
 i++;
}

 for  do-while

打印9*9乘法表
var str = '';
for (var i = 1; i <= 9; i++) {
for (var j = i; j <=9; j++) {
str += i + ' * ' + j + ' = ' + i * j + '\t';
}
str += '\n';
}
console.log(str)

 调试

浏览器中按F12-->sources-->找到需要调试的文件-->在程序的某一行设置断点

Watch: 监视,通过watch可以监视变量的值的变化,非常的常用。
F10: 程序单步执行,让程序一行一行的执行,这个时候,观察watch中变量的值的变化。
F8:跳到下一个断点处,如果后面没有断点了,则程序执行结束

 监视变量,不要监视表达式,因为监视了表达式,那么这个表达式也会执行

数组的定义

数组是一个有序的列表,可以在数组中存放任意的数据,并且数组的长度可以动态的调整。

 创建方式

通过实例化创建   基本不用

var arr = new Array(3); //只写一个参数,创建一个长度为3的数组 , 数组中每一项都为 空置empty [empty,empty,empty]
arr = new Array(1,2,3,4); //多个参数 会生成对应参数个数与内容的数组 [1,2,3,4]

字面量其实就是指变量的常数值

在JS中,以特定符号或格式规定的,创建指定类型变量的,不能被修改的便捷表达式。 因为是表达式,字面量都有返回值。 字面量是方便程序员以简单的句式,创建对象或变量的语法糖,但有时候以字面量创建的“对象”和以标准方法创建的对象的行为并不完全相同 

基本数据类型的字面量就是自己

引用型就有  {}  []  等

null  有点特殊

通过数组字面量创建数组

// 创建一个空数组
var arr1 = [];
// 创建一个包含3个数值的数组,多个数组项以逗号隔开
var arr2 = [1, 3, 4];
// 创建一个包含2个字符串的数组
var arr3 = ['a', 'c']

 for in 遍历(不推荐)

for(var key in arr){
  console.log(key,arr[key]);  //key 为下标 arr[key]为对应key下标的值
}
使用for-in可以遍历数组,但是会存在以下问题:
1.index索引为字符串型数字(注意,非数字),不能直接进行几何运算。
2.遍历顺序有可能不是按照实际数组的内部顺序(可能按照随机顺序)。
3.使用for-in会遍历数组所有的可枚举属性,包括原型。例如上例的原型方法method和name属性都会被遍历出来,通常需
要配合hasOwnProperty()方法判断某个属性是否该对象的实例属性,来将原型对象从循环中剔除。

 for of 遍历    数组

for(var key of arr){
  console.log(key);
}
相比 for-in 不会出现顺序错乱的问题 也不会遍历出所有可枚举属性

 数组类型判定与隐式转换

var arr = [1,2,3];
typeof arr //'object'
Number(arr) //NaN
String(arr) // '1,2,3'
Bollean(arr) // true
[] == [] //false
arr + '海牙' //'1,2,3海牙'
arr / 2 // NaN
arr + [] // '1,2,3'
[] + [] //''
[2]  - 1 //1
[1,]  - 1 //0

[1,2]  - 1 // NaN
!![] // true

 数组基础方法

Array.isArray([1, 2, 3]); 
// true
Array.isArray({foo: 123});
// false
Array.isArray("foobar"); 
// false
Array.isArray(undefined); 
// fals

 join(separator)
方法将一个数组的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符

separator 可选 指定分隔符号 该参数默认值为","

const elements = ['火', '空气', '水'];
console.log(elements.join());
// "火,空气,水"
console.log(elements.join(''));
// "火空气水"
console.log(elements.join('-'));
// "火-空气-水"

一个所有数组元素连接的字符串。如果 arr.length 为0,则返回空字符串。

如果一个元素为  undefined 或  null ,它会被转换为空字符串''。

 push(element1,...,elementN)

方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。改变原数组

 unshift(element1,...,elementN)
方法将一个或多个元素添加到数组的前端,并返回该数组的新长度 改变原数组

pop()

方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度

shift()
方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。

var animals = ['猪', '狗', '牛'];
var count = animals.push('大象');
console.log(count);
// 4
console.log(animals);
// ['猪', '狗', '牛','大象']
animals.push('鸡', '鸭', '鹅');
console.log(animals);
//['猪', '狗', '牛', '大象', '鸡', '鸭', '鹅'

slice()  返回一个含有被提取元素的新数组   不改变原数组

切割方法返回一个新的数组对象,这一对象是一个由  begin 和  end 决定的原数组的浅拷贝(包括  begin (起始下标),不包括 end (结束下标))。原始数组不会被改变

var plants = ['西红柿', '黄瓜', '芹菜', '豆角', '土豆'];
console.log(plants.slice(2));
// ['芹菜', '豆角', '土豆']
console.log(plants.slice(2, 4));
// [ '芹菜', '豆角']
console.log(plants.slice(1, 5));
// ['黄瓜', '芹菜', '豆角', '土豆']

concat()     合并方法用于合并两个或多个数组。不改变原数组,而是返回一个新数组

var plants = ['西红柿', '黄瓜', '芹菜', '豆角', '土豆'];
var otherPlants = ['冬瓜', '韭菜']
var newPlants = plants.concat(otherPlants);
console.log(newPlants);
//['西红柿', '黄瓜', '芹菜', '豆角', '土豆', '冬瓜', '韭菜']

indexOf()          不改变原数组 存在返回其索引

方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。

var plants = ['西红柿', '黄瓜', '芹菜', '豆角', '土豆','黄瓜'];
console.log(plants.indexOf('黄瓜'));
//1
console.log(plants.indexOf('黄瓜',3));
//5
console.log(plants.indexOf('大象'));
//-1

 字符串基础方法

charAt()   方法从一个字符串中返回指定的字符

如果指定的 index 值超出了该范围,则返回一个空字符串

var str ='你好,我是海牙老师';
console.log(str.charAt(3));
//返回了 str[3]的值

indexOf()

方法返回在字符串中可以找到一个给定字符的第一个首字对应索引,如果不存在,则返回-1。

var plants = '西红柿,黄瓜,芹菜,豆角,土豆,黄瓜'; //length 18
console.log(plants.indexOf('黄瓜'));
//4
console.log(plants.indexOf('黄瓜',5));
//16
console.log(plants.indexOf('黄'));
//4
console.log(plants.indexOf('黄瓶'));
//-1
console.log(plants.indexOf('大象'));
//-1
console.log(plants.indexOf(''));
//0
console.log(plants.indexOf('',5));
//5
console.log(plants.indexOf('',19));
//18 超出长度的参数会返回长度

split()
方法使用指定的分隔符字符串将一个String对象分割成子字符串数组,以一个指定的分割字串来决定每个拆分的位置。

var str ='你好,我是海牙老师';
var arr = str.split(',');
console.log(arr);
// ['你好','我是海牙老师']
var str ='你好,我是海牙老师';
var arr = str.split('');
console.log(arr);
// ["你", "好", ",", "我", "是", "海", "牙", "老", "师"]
var str ='你好,我是海牙老师';
var arr = str.split();
console.log(arr);
//["你好,我是海牙老师"]
var str ='你好,我,是海,牙老,师';
var arr = str.split(',',3);    只返回3个

console.log(arr);
// ["你好", "我", "是海"]

slice()

方法提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串。

var str = 'The quick';

        console.log(str.slice(2));

        // "e quick"      索引为2开始 后面都要

        console.log(str.slice(3, 5));

        // " q"          索引为3是空 不包含索引为5   空格+q

        console.log(str.slice(-4));

        // "uick"        相当于 9-4  从索引为5开始后面都要  u的索引为5

        console.log(str.slice(-6, -2));

        //  qui          9-6 9-2   索引3,7    3是空格7是c  空格+qui

        console.log(str.length);

        // 9

trim()    不改变原字符串   返回一个新的字符串

方法会从一个字符串的两端删除空白字符。在这个上下文中的空白字符是所有的空白字符 (space, tab, no-break space 等) 以及所有行终止符字符(如 LF,CR等

 函数定义
JavaScript 函数是被设计为执行特定任务的代码块。
JavaScript 函数会在某代码调用它时被执行

函数声明  函数表达式

function 函数名(){
 // 函数体
}

var 变量名 = function() {
 // 函数体
}

什么是函数?
把一段相对独立的具有特定功能的代码块封装起来,形成一个独立实体,就是函数,起个名字(函数名),在后续开发中可以反复调用函数的基础作用就是封装一段代码,将来可以重复使用。我们称之为 封装

返回值详解:
如果函数没有显示的使用 return语句 ,那么函数有默认的返回值:undefined
如果函数使用 return语句,那么跟再return后面的值,就成了函数的返回值
如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined
函数使用return语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所有其他代码都不会再执行。

arguments
JavaScript 函数有一个名为 arguments 对象的内置对象。
arguments 对象包含函数调用时使用的参数数组。
arguments为 (类数组)这样,您就可以简单地使用函数来查找(例如)数字列表中的最高值

jsx = findMax(1, 123, 500, 115, 44, 88);
function findMax() { 
  var i;  
  var max = -Infinity;
  for (i = 0; i < arguments.length; i++) {
    if (arguments[i] > max) { 
      max = arguments[i];   
   } 
 }
  return max;
}

 函数分类

纯函数

1. 如果函数的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据
的变化,必须只依赖于其输入参数。
2. 该函数不会产生任何可观察的副作用

function padLeft(num) {
  return String(num)[1] && String(num) || '0' + num;
}

非纯函数(函数副作用)

所谓"副作用"(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果

var str = '海牙';
function changeStr(){
str = 'kyogre';
}
console.log(str); //海牙
change();
console.log(str); //kyogre

匿名函数

将匿名函数赋值给一个变量,这样就可以通过变量进行调用匿名函数自调用

const test = function() {}

自调用函数

匿名函数(IIFE)不能通过直接调用来执行,因此可以通过匿名函数的自调用的方式来执行

(function () {
 alert(123);
})();

柯理化函数

//纯函数
function add(x, y) {
  return x + y;
}
console.log(add(1, 2)); // 3
//柯理化
function curryingOfAdd(x) {
  return function (y) {
    return x + y;
 }
}
console.log(curryingOfAdd(1)(2)); // 3
var tranCurry = add(3);
tranCurry(2); //5
tranCurry(8); //11

作用域

 全局变量和局部变量

不使用var声明的变量是全局变量,不推荐使用。
变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁

块级作用域
任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域

词法作用域

变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域

在 js 中词法作用域规则:
函数允许访问函数外的数据.
整个代码结构中只有函数可以限定作用域.
作用域规则首先使用提升规则分析
如果当前作用规则中有名字了, 就不考虑外面的名字

var num = 123;
function foo() {
 console.log( num );   先在函数中找,没有就去全局找
}
foo();      

if ( false ) {
  var num = 123;
}
console.log(num); // undefiend    先在全局找没有就undefined

作用域链

function f1() {
  var num = 123;
  function f2() {
    console.log( num );
 }
  f2();
}
var num = 456;
f1();    最后打印一次123   函数调用找到num的值 发现函数2 调用函数2打印一次123

对象obj

 JavaScript是一门基于对象的语言

javascript中 我们称Object为 对象 对象的概念也分广义和狭义:广义上javascript中处处是对象,狭义指的是我们通过{}字面量创建的对象

JavaScript的对象是无序属性的集合

字面量

字面量(literal)是用于表达源代码中一个固定值的表示法(notation).

字面量(literal),在高级语言中 我们可以通过更直观更高效的方式直接赋予变量 具体  值 , 当需要使用值得时候 才会去根据值得类型和内容进行包装解析; 先存储 后解释 

javascript中字面量包括

1. 字符串字面量(String Literal)

var str = '张晓华'; //张晓华 就是字符串字面量

2.数组字面量(array literal)

var arr = [1,2,3,4,5]; //[1,2,3,4,5] 就是数组字面量

3.对象字面量(object literal)

var obj = {
  name:'橘子',
  age: 1,
  favorite: '小鱼干'

4.函数字面量(function literal)

var fn = function(){
  alert('你好');
}

 javascript中的内置对象

JavaScript中的对象分为3种:内置对象、浏览器对象、自定义对象
javascript是基于对象的语言, javascript不可以自己创建一个原生对象,只能访问内置对象属性和调用已有的内置对象方法。但是可以通过基于Object创建狭义概念上的 对象 。

 创建对象方式(自定义)

创建对象有三种基础方式: 对象字面量 原生对象实例化 自定义构造函数

字面量对象

var myCat = {
  name: '橘子',
  color: 'orange',
  age: 1,
  favorite: '小鱼干',
  speak: function(){
 console.log('喵~~喵~喵~~~');
 }

原生对象实例化 new Object()

var myCat = new Object();
myCat.name = '橘子';
myCat.color = 'orange';
myCat.age = 1;
myCat.favorite = '小鱼干';
myCat.speak = function(){
  console.log('喵~~喵~喵~~~');
}

工厂函数创建对象

function createCat(name,age,color){
  var cat = new Object();
  cat.name = name;
  cat.color = color;
  cat.age = age;
  cat.favorite = '小鱼干';
  cat.speak = function(){
    console.log('喵~~喵~喵~~~');
 }
  return cat;
}
var myCat = createCat('橘子',1,'orange')

自定义构造函数

function Cat(name,age,color,favorite){
  this.name = name;
  this.age = age;
  this.color = color;
  this.favorite = favorite;
  this.speak = function(){
   console.log('喵~~喵~喵~~~');
 }
}
var myCat = new Cat('橘子',1,'orange','小鱼干');

 instanceof

在 JavaScript 中,判断一个变量的类型尝尝会用 typeof 运算符,在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 “object”。ECMAScript 引入了另一个 Java 运算符instanceof 来解决这个问题。instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。例如: 还有注意事项

var oStringObject = new String("hello world");
console.log(oStringObject instanceof String);    // 输出 "true"

 构造函数

面向对象编程’的第一步,就是要生成对象。而js中面向对象编程是基于构造函数(constructor)和原型链(prototype)的。

function Person(name,age,sex){ //Person就是构造函数
  this.name = name;
  this.age = age;
  this.sex = sex;
  this.speak = function(){
    console.log('我叫' + this.name + ',今年:' + this.age + '岁,性别:' + this.sex);
 }
}

a:构造函数的函数名的第一个字母通常大写。
b:函数体内使用this关键字,代表所要生成的对象实例。
c:生成对象的时候,必须使用new命令来调用构造函数。

 new关键字

构造函数 ,是一种特殊的函数。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中

new在执行时会做四件事情

1 创建一个空对象,作为将要返回的对象实例。
2 将空对象的原型指向了构造函数的prototype属性。
3 将空对象赋值给构造函数内部的this关键字。
4 开始执行构造函数内部的代码

 this详解

JavaScript中的this指向问题,有时候会让人难以捉摸,随着学习的深入,我们可以逐渐了解
现在我们需要掌握函数内部的this几个特点
1. 函数在定义的时候this是不确定的,只有在调用的时候才可以确定
2. 一般函数直接执行,内部this指向全局window
3. 函数作为一个对象的方法,被该对象所调用,那么this指向的是该对象
4. 构造函数中的this其实是一个隐式对象,类似一个初始化的模型,所有方法和属性都挂载到了这个隐式对象身上,后续通过new关键字来调用,从而实现实例化

 遍历对象的属性
通过for..in语法可以遍历一个对象

const obj = {

            a: 123,

            b: '443',

            c: [1, 2, 3]

        };

        for (let key in obj) {

            console.log('key=' + key, '----' + obj[key]);

        }

key=a ----123
jstest.html:19 key=b ----443
jstest.html:19 key=c ----1,2,3

得到对象的 键=key  值=obj[key]

 删除对象的属性

function fun() {
 this.name = 'mm';
}
var obj = new fun();
console.log(obj.name); // mm
delete obj.name;
console.log(obj.name); // undefined

 JSON格式对象

JSON即Javascript对象表示方法 (Javascript Object Notation) ,也就是通过字面量来表示一个对象:

JSON 英文全称 JavaScript Object Notation

JSON 是一种轻量级的数据交换格式。

它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集

JSON数据使用’”键名”:”值”’的形式,其中键名要求是字符串,而值 可以是以下任意类型:
1. 数值(整数,浮点数)
2. 字符串(在双引号中)
3. 逻辑值(true/false)
4. 数组(在方括号中)
5. 对象(在花括号中)

var studentList = [
  { "name": "张三", "age": 18, "sex": 1 },
  { "name": "李思", "age": 19, "sex": 0 },
  { "name": "王武", "age": 98, "sex": 1 },
  { "name": "赵柳", "age": 77, "sex": 0 }
]

JSON.parse() 反序列化

将JSON数据解析为Javascript对象

方法用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。提供可选的 reviver 函数用以在返回之前对所得到的对象执行变换(操作)

var json = '{"result":true, "count":42}';
var obj = JSON.parse(json);
console.log(obj.count);
//42
console.log(obj.result);
// true

返回值    Object 类型, 对应给定 JSON 文本的对象/值

异常   若传入的字符串不符合 JSON 规范,则会抛出 SyntaxError 异常

JSON.stringify() 序列化

方法将一个 JavaScript 对象或值转换为 JSON 字符串,如果指定了一个 replacer 函数,则可以选择性地替换值,或者指定的 replacer 是数组,则可选择性地仅包含数组指定的属性

console.log(JSON.stringify({ x: 5, y: 6 }));
// "{"x":5,"y":6}"
console.log(JSON.stringify([new Number(3), new String('false'), new Boolean(false)]));
//"[3,"false",false]"
console.log(JSON.stringify({ x: [10, undefined, function(){}, Symbol('')] }));
// "{"x":[10,null,null,null]}"
console.log(JSON.stringify(new Date(2006, 0, 2, 15, 4, 5)));
// ""2006-01-02T15:04:05.000Z"

返回值  一个表示给定值的JSON字符串。

异常  当发现循环引用时,抛出类型错误TypeError(“循环对象值”)异常。
当试图将BigInt (BigInt 为javascript中Number能表示的最大数 2^53 - 1 )值字符串化时,会抛出类型错误 TypeError(“BigInt
值不能在JSON中序列化”)

 执行上下文

简而言之,执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行

执行上下文的类型

全局执行上下文: 这是默认的、最基础的执行上下文。不在任何函数中的代码都位于全局执行上下文中。它做了两件事:1. 创建一个全局对象,在浏览器中这个全局对象就是 window 对象。2. 将 this 指针指向这个全局对象。一个程序中只能存在一个全局执行上下文。


函数执行上下文: 每次调用函数时,都会为该函数创建一个新的执行上下文。每个函数都拥有自己的执行上下文,但是只有在函数被调用的时候才会被创建。一个程序中可以存在任意数量的函数执行上下文。每当一个新的执行上下文被创建,它都会按照特定的顺序执行一系列步骤,具体过程将在本文后面讨论。


Eval 函数执行上下文: 运行在 eval 函数中的代码也获得了自己的执行上下文,但由于eval是魔鬼 我们一般不通过eval进行开发操作。

 变量提升和 this 指向的细节

变量声明提升

大部分编程语言都是先声明变量再使用,但在 JS 中,事情有些不一样

console.log(a); // undefined
var a = 10;

上述代码正常输出 undefined 而不是报错 Uncaught ReferenceError: a is not defined ,这是因为声明提升

var a; //声明 默认值是undefined “准备工作”
console.log(a);
a = 10; //赋值

函数声明提升

我们都知道,创建一个函数的方法有两种,一种是通过函数声明 function foo(){} 另一种是通过函数表达式 var foo =function(){} ,那这两种在函数提升有什么区别呢

console.log(f1); // function f1(){}
function f1() {} // 函数声明
console.log(f2); // undefined
var f2 = function() {}; // 函数表达式

我们前面说过变量和函数都会上升,遇到函数表达式  var f2 = function(){} 时,首先会将 var f2 上升到函数体顶部,然而此时的 f2 的值为 undefined,所以执行 foo() 报错。
而对于函数 f1() , 则是提升了整个函数,所以f1() 才能够顺利执行

暂时不去想函数名和变量重名的情况

 确定 this 的指向

先搞明白一个很重要的概念 —— this 的值是在执行的时候才能确认,定义的时候不能确认! 为什么呢 —— 因为this 是执行上下文环境的一部分,而执行上下文需要在代码执行之前确定,而不是定义的时候。看如下例子

// 情况1
function foo() {
 console.log(this.a) //1
}
var a = 1
foo()
// 情况2
function fn(){
 console.log(this);
}
var obj={fn:fn};
obj.fn(); //this->obj
// 情况3
function CreateJsPerson(name,age){
//this是当前类的一个实例p1
this.name=name; //=>p1.name=name
this.age=age; //=>p1.age=age
}
var p1=new CreateJsPerson("海牙",18);
//情况4
var obj = {
  name:'海牙',
  showName:function(){
    console.log(this.name); // 海牙 this => obj

          (function(){
      console.log(this.name); //undefined this=>window
   })();
 }
}
obj.showName()

 执行上下文栈(Execution Context Stack)

函数多了,就有多个函数执行上下文,每次调用函数创建一个新的执行上下文,那如何管理创建的那么多执行上下文呢?
JavaScript 引擎创建了执行上下文栈来管理执行上下文。可以把执行上下文栈认为是一个存储函数调用的栈结构,遵循先进后出的原则

JavaScript 执行在单线程上,所有的代码都是排队执行。
一开始浏览器执行全局的代码时,首先创建全局的执行上下文,压入执行栈的顶部。
每当进入一个函数的执行就会创建函数的执行上下文,并且把它压入执行栈的顶部。当前函数执行完成后,当前函数的执行上下文出栈,并等待垃圾回收。浏览器的 JS 执行引擎总是访问栈顶的执行上下文。全局上下文只有唯一的一个,它在浏览器关闭时出栈。

 包装对象

基础类型undefined、null、boolean、number、string和symbol 都是非引用类型(非对象)

var str = '你好';
//var newStr = new String('你好'); 包装 实例化对象
var arr = str.split('');
//var arr = newStr.split(''); 通过包装对象调用对象方法
// newStr = null; 销毁包装对象
console.log(arr);
// ["你", "好"]

Object方法

 Object.is(v1,v2)    方法判断两个值是否为同一个值  返回布尔值

Object.is() 方法判断两个值是否为同一个值。如果满足以下条件则两个值相等:
都是 undefined
都是 null
都是 true 或 false
都是相同长度的字符串且相同字符按相同顺序排列
都是相同对象(意味着每个对象有同一个引用)
都是数字且
都是 +0
都是 -0
都是 NaN
或都是非零而且非 NaN 且为同一个值
与== 运算不同。  == 运算符在判断相等前对两边的变量(如果它们不是同一类型) 进行强制转换 (这种行为的结果会将 "" ==
false 判断为 true), 而 Object.is不会强制转换两边的值。
与=== 运算也不相同。 === 运算符 (也包括 == 运算符) 将数字 -0 和 +0 视为相等 ,而将Number.NaN 与NaN视为不相等.

Object.freeze(obj)   返回被冻结的对象

方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。 freeze() 返回和传入的参数相同的对象

Object.assign(target, ...sources)   返回目标对象
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象

var target = { a: 1, b: 2 };
var source = { b: 4, c: 5 };
var returnedTarget = Object.assign(target, source);
console.log(target);
// { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
// { a: 1, b: 4, c: 5 }
如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象
的属性。

Object.keys() 返回 一个表示给定对象的所有可枚举属性的字符串数组。
方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致

Object.keys({a:1,b:2,c:3,d:5});
// ["a", "b", "c", "d"]
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr));
// ['0', '1', '2']

Object.keys 返回一个所有元素为字符串的数组,其元素来自于从给定的object上面可直接枚举的属性。这些属性的顺序与手动遍历该对象属性时的一致

Array方法

forEach()  方法对数组的每个元素执行一次给定的函数

var items = ['item1', 'item2', 'item3'];
var copy = [];
// for写法
for (var i=0; i<items.length; i++) {
 copy.push(items[i]);
}
// forEach方法   返回值undefined
items.forEach(function(item){ //function匿名函数作为实参
 copy.push(item);
})

arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
callback
为数组中每个元素执行的函数,该函数接收一至三个参数:
  currentValue
  数组中正在处理的当前元素。
  index 可选
  数组中正在处理的当前元素的索引。
  array 可选
  forEach() 方法正在操作的数组。
thisArg 可选
可选参数。当执行回调函数 callback 时,用作 this 的值

sort()      返回排序后的数组。改变原数组

方法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的

const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// ["Dec", "Feb", "Jan", "March"]
const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// [1, 100000, 21, 30, 4]
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
 return a - b;
});
console.log(numbers);
// [1, 2, 3, 4, 5]
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
 return b - a;
});
console.log(numbers);
// [5, 4, 3, 2, 1]
如果没有指明 排序函数 ,那么元素会按照转换为的字符串的诸个字符的Unicode位点进行排序。例如 "Banana" 会被排列
到 "cherry" 之前。当数字按由小到大排序时,9 出现在 80 之前,但因为(没有指明 排序函数),比较的数字会先被转换
为字符串,所以在Unicode顺序上 "80" 要比 "9" 要靠前

map()
方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值

var originArr = [1,2,3,4,5];
var newArr = originArr.map(function(current,index,array){
  return current * 2;
});
console.log(newArr);
//[2,4,6,8,10]
---------------------------------------------------
var kvArray = [{key: 1, value: 10},
       {key: 2, value: 20},
       {key: 3, value: 30}];
var reformattedArray = kvArray.map(function(obj) {
 var rObj = {};
 rObj[obj['key']] = obj.value;
 return rObj;
});
console.log(reformattedArray);
// [{1: 10}, {2: 20}, {3: 30}],
map 方法会给原数组中的每个元素都按顺序调用一次  callback 函数。callback 每次执行后的返回值(包括 undefined)组
合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不
会被调用。
callback 函数会被自动传入三个参数:数组元素,元素索引,原数组本身。
map 不修改调用它的原数组本身(当然可以在 callback 执行时改变原数组)

filter()   不改变原数组

返回值 一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。

方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素

var arr = [
{ id: 15 },
{ id: -1 },
{ id: 0 },
{ id: 3 },
{ id: 12.2 },
{ },
{ id: null },
{ id: NaN },
{ id: 'undefined' }
];
function isNumber(obj) {
 return obj !== undefined && typeof(obj) === 'number' && !isNaN(obj);
}
var arrByID = arr.filter(function(item) {
  return isNumber(item.id) && item.id !== 0;
});
console.log(arrByID);
// [{ id: 15 }, { id: -1 }, { id: 3 }, { id: 12.2 }

reduce()  返回 累加后的结果

方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。

var originArr = [3,4,5,6,7,8];
var sum = originArr.reduce(function(acc,current,idx,arr){

//  acc 累加器  current 当前元素  index 当前所以  arr 当前数组

//  第一次进来时候 acc=0 第二次进来 acc=3
  return acc + current;
});
console.log(sum);
//33 数组中每项累加和
var str = 'jfkldsajgklasjkhlgjefaklhjaerkl';
var statistics = str.split('').reduce(function(acc,current){
  if(acc[current]){
    acc[current]++;
 }else {
    acc[current] = 1;
 }
  return acc;
},{});

 lastIndexOf()

indexOf()  

方法返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从  fromIndex 处开始

 reverse()

方法将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组

var array1 = ['one', 'two', 'three'];
console.log( array1);
// ["one", "two", "three"]
var reversed = array1.reverse();
console.log( reversed);
// ["three", "two", "one"]
console.log(array1);
// ["three", "two", "one"]
reverse会对原数组造成修改影响

 includes()

方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。

var array1 = [1, 2, 3];
console.log(array1.includes(2));
// expected output: true
var pets = ['cat', 'dog', 'bat'];
console.log(pets.includes('cat'));
// expected output: true
console.log(pets.includes('at'));
// expected output: false

flat()  返回一个包含将数组与子数组中所有元素的新数组。

 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回

var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
//使用 Infinity,可展开任意深度的嵌套数组
var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
//二维展开替代方案
var arr = [1, 2, [3, 4]];
var flatArr = arr.reduce(function(acc,curr){
  return acc.concat(curr);
},[]);
console.log(flatArr);
//[1, 2, 3, 4]

 方法汇总

toString()  把数组转换成字符串,逗号分隔每一项
valueOf() 返回数组对象值本身
// 1 栈操作(先进后出)
push()
pop() //取出数组中的最后一项,修改length属性
// 2 队列操作(先进先出

push()
shift()  //取出数组中的第一个元素,修改length属性
unshift() //在数组最前面插入项,返回数组的长度
// 3 排序方法
reverse()  //翻转数组
sort(); //即使是数组sort也是根据字符,从小到大排序
// 带参数的sort是如何实现的?
// 4 操作方法
concat() //把参数拼接到当前数组
slice() //从当前数组中截取一个新的数组,不影响原来的数组,参数start从0开始,end从1开始
splice() //删除或替换当前数组的某些项目,参数start, deleteCount, options(要替换的项目)
// 5 位置方法
indexOf()、lastIndexOf() //如果没找到返回-1
// 6 迭代方法 不会修改原数组(可选)
every()、filter()、forEach()、map()、reduce()、some()
// 7 方法将数组的所有元素连接到一个字符串中。
join(

String

 charCodeAt()   方法返回  0 到  65535 之间的整数,表示给定索引处的 UTF-16 代码单元

replace()   替换

slice()
方法提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串。

 方法汇总
// 1 字符方法
charAt() //获取指定位置处字符
charCodeAt() //获取指定位置处字符的ASCII码
str[0] //HTML5,IE8+支持 和charAt()等效
// 2 字符串操作方法
concat() //拼接字符串,等效于+,+更常用
slice() //从start位置开始,截取到end位置,end取不到
substring() //从start位置开始,截取到end位置,end取不到
substr() //从start位置开始,截取length个字符
// 3 位置方法
indexOf() //返回指定内容在元字符串中的位置
lastIndexOf() //从后往前找,只找第一个匹配的
// 4 去除空白
trim() //只能去除字符串前后的空白
// 5 大小写转换方法
to(Locale)UpperCase() //转换大写
to(Locale)LowerCase() //转换小写
// 6 其它
search()
replace()
split()
fromCharCode() //unicode转字符串
// String.fromCharCode(101, 102, 103);

 Math对象方法

Math.max()

Math.round() 函数返回一个数字四舍五入后最接近的整数

Math.floor()   返回小于或等于一个给定数字的最大整数 : 向下取整

Math.ceil()  函数返回大于或等于一个给定数字的最小整数

 Date对象方法

// 获取当前时间,UTC世界时间,距1970年1月1日(世界标准时间)起的毫秒数
var now = new Date();
console.log(now.valueOf()); // 获取距1970年1月1日(世界标准时间)起的毫秒数
Date构造函数的参数
1. 毫秒数 1498099000356 new Date(1498099000356)
2. 日期格式字符串  '2015-5-1' new Date('2015-5-1')
3. 年、月、日……  new Date(2015, 4, 1)  // 月份从0开始

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaodunmeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值