前端学习之 JavaScript

一. JS中可以表示的数字的最大值和大于零的最小值

  • Number.MAX_VALUE和Number.MIN_VALUE
  • 如果比Number.MAX_VALUE大,就用Infinity(正无穷大)表示,-Infinity表示(负无穷大)
  • NAN是一个特殊的数字,表示Not A Number
  • 使用typeof检查Number.MAX_VALUE,NaN和Infinity得到结果number
  • 尽量不要使用JS进行浮点数的计算,特别是对精确度要求高的情况下,需要通过后端去计算
    详情见为什么JavaScript里面0.1+0.2 === 0.3是false

二. 强制类型转换

1.String()和toString()的区别:

toString()的用法:

let a = 123;
a.toString()

toString()不能将值为null和undefined的变量变为字符串
String()的用法:

let a = 123;
String(a)

String()可以将值为null和undefined的变量变为字符串

2.Number()、parseInt()和parseFloat()的区别:

Number:
1.如果是纯数字的字符串,可直接转为数字
2.如果带有非数字的字符串,则转为NaN
3.如果是空的字符串或者全是空格,则转为0
4.如果boolean值为true,则转为1,若为false,则转为0
5.如果值为null,转为0
6.如果值为undefined,转为NaN

parseInt:
可以将一个字符串中,首位开始的有效整数取出

let a = '123px';
a = parseInt(a);   //a = 123
let b = '123.456px';
b = parseInt(b);   //b = 123
let c = '123b456px';
c = parseInt(c);   //c = 123
let d = 'b123px';
d = parseInt(d);   //d = NaN

parseFloat:
可以将一个字符串中,首位开始的有效浮点数取出

let a = '123.456px';
a = parseFloat(a);   //a = 123.456
let b = '123.456.789px';
b = parseFloat(b);   //b = 123.456
let c = '123.456b456px';
c = parseFloat(c);   //c = 123.456
let d = 'b123.456px';
d = parseFloat(d);   //d = NaN
let e = '123px';
e = parseFloat(e);   //e = 123

如果对非String类型的变量使用parseInt和parseFloat,方法会先将变量转为String类型,然后在操作

let a = true;
a = parseFloat(a);   //a = NaN
a = parseInt(a);     //a = NaN
let b = null;
a = parseFloat(b);   //a = NaN
a = parseInt(b);     //a = NaN
let c = undefined;
a = parseFloat(c);   //a = NaN
a = parseInt(c);     //a = NaN
let d = 198.123;
d = parseInt(d);     //d = 198   可以用来给浮点数取整
let e = 070;         //0开头代表八进制的数字,0x开头代表十六进制的数字
e = parseInt(e, 10); //e = 56 第二个参数指定数字的进制
3.Boolean()
  1. 数字----->布尔
    除了0和NaN,其他都是true

  2. 字符串----->布尔
    除了空串,其他都是true

  3. null和undefined都会转换为false

  4. 对象也会转换为true

  5. if()会隐式转换括号中的判断条件为布尔类型,和Boolean()作用一致

三.基础运算符

  1. 任何值和字符串相加,都会转化字符串,并作拼串操作。我们可以利用这一特点,将任意数据类型转换为字符串,只需要加上一个空串""就行;
let a = 123;
a = a + "";
console.log(typeof(a));   // string
let resultOne = 1 + 2 + "3";   //ruseltOne = "33"
let resultTwo = "1" +2 + 3;    //reseltTwo = "123"
  1. 任何值做 - * / 运算时都会自动转换为Number
  2. 对于非Number类型的值,可以使用+,将其转换为number
let a = "123";
a = +a;     //a = 123(number类型)
let result = 1 + +'2' + 3;    //result = 6
  1. 自增(++)和自减(- -)
  • 自增(++):

通过自增可以使变量在自身的基础上增加1

对于一个变量自增以后,原变量的值会立即自增1

自增分成两种:后++(a++)和前++(++a)

无论是a++ 还是++a,都会立即使原变量的值自增1

不同的是a++和++a的值不同

a++的值等于原值(自增前的值)
++a的值等于新值(自增后的值)

let a = 10;
let resultOne = a++ + ++a + a;    //resultOne = 10 + 12 + 12 = 34
a = a++;     //a = 10
  • 自减(–):

通过自减可以使变量在自身的基础上减1

自减分成两种:后- -(a- -)和前- -(- -a)

无论是a- -还是- -a都会立即使原变量的值自减1

不同的是a- -和- -a的值不同

a- -的值等于原值(自减前的值)
- -a的值等于新值(自减后的值)

  1. 关系运算符(分别为小于、大于、小于等于、大于等于、等于、不等于)
  • 比较两个字符串的时候是比较字符的编码,且只比较第一个,若第一个相同,在比较第二个,以此类推
  • 因为英文比较也是通过编码进行比较,所以可用来排序
  • 一个数字和非数值的比较,会将转为数字在比较,任何和NaN的相比都是false
  • 若一个字符串的数字和number类型的数字相比,会自动转型,但是两个字符串的数字相比会有不可预期的结果
  1. 编码
    这里说UTF-8,也称万国码,每一个文字或字母,都用四位十六进制的数字来表示,
    若我们想使用一些不常用的符号,可通过\u+该符号编码显示
console.log('\u2620')

在这里插入图片描述

  1. 相等运算符
  • undefined == null
console.log(undefined == null) // true
  • NaN不和任何值相等,包括它本身
console.log(NaN == NaN) // false
  • ===全等运算符,和 == 相等运算符的区别:
    相等运算符会自动进行类型转换,全等运算符则不会
console.log(undefined == null) // true
console.log(undefined === null) // false
  • !== 不全等运算符,和 != 不等运算符的区别:
    不等运算符会自动进行类型转换,不全等运算符则不会
console.log(1 != '1') // false
console.log(1 !== '1') // true
  1. 条件运算符

条件运算符也叫三元运算符

语法:
条件表达式?语句1:语句2;

执行的流程:
条件运算符在执行时,首先对条件表达式进行求值,
如果该值为true,则执行语句1,并返回执行结果,如果该值为false,则执行语句2,并返回执行结果

四.循环

  1. break的作用:
  • break用来退出switch和循环语句
  • break只对最近的循环语句进行中断
for(var i=0;i<5;i++){
    
    
	console.log("外层循环:"+i);
	for(var j=0;j<5;j++){
    
    
		break;
		console.log("内层循环:"+j);
	}
}

在这里插入图片描述

  • break可以中断指定名称的循环
outer: /*outer是第一个for循环的名称*/
for(var i=0;i<5;i++){
    
    
	console.log("外层循环:"+i);
	for(var j=0;j<5;j++){
    
    
		break outer;
		console.log("内层循环:"+j);
	}
}

在这里插入图片描述

  1. continue的作用:
  • continue用来跳过当次循环
for(var i = 0;i<5;i++){
    
    
	if(i == 2){
    
    
		continue;
	}
	console.log(i);
}

在这里插入图片描述

  • continue只对最近的循环语句进行中断
  • continue可以中断指定名称的循环
  1. 通过break提示循环的效率
//测试如下的程序的性能
//在程序执行前,开启计时器
//console.time( "计时器的名字")可以用来开启一个计时器
//它需要一个字符串作为参数,这个字符串将会作为计时器的标识
console.time( "test" );
//打印2-10000之间所有的质数
for(var i = 2;i<10000;i++){
    
    
	var flag = true;
	for(var j = 2;j<i;j++){
    
    
		if(i%j ==0){
    
    
			//如果进入判断则证明 i不是质数,修改flag值为false
			flag = false;
			//一旦进入判断,则证明 i不可能是质数了,此时循环再执行已经没有任何意义了
			//使用break来结束循环
			break;
		}
	}
	//如果是质数,则打印 i的值
	if(flag){
    
    
		//console.log(i); //为了测试使用和不使用break的性能,去掉输出,减少输出时间,减少误差
	}
}
// console.timend()用来停止一个计时器,需要一个计时器的名字作为参数
console.timeEnd( "test" );

使用break:
在这里插入图片描述
不使用break:
在这里插入图片描述

五.对象

1.JS中数据类型:
  • String字符串
  • Number数值
  • Boolean布尔值
  • Null空值
  • Undefined未定义

以上这五种类型属于基本数据类型,以后我们看到的值,只要不是上边的5种,全都是对象

object对象

如果使用基本数据类型的数据,我们所创建的变量都是独立,不能成为一个整体。

对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性。

对象的分类:

  1. 内建对象
    由ES标准中定义的对象,在任何的ES的实现中都可以使用
    比如:Math String Number Boolean Function 0bject… . .

  2. 宿主对象
    由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象
    比如 BOM DOM。
    console.log()中的console是一个对象
    document.write()中的document也是一个对象

  3. 自定义对象
    由开发人员自己创建的对象

2.操作对象
  1. 创建对象
    使用new关键字调用的函数,是构造函数constructor
    构造函数是专门用来创建对象的函数
var obj = new Object();
//使用typeof检查一个对象时,会返回object
console.log(typeof obj)
  1. 添加属性
    在对象中保存的值称为属性
    向对象添加属性
    语法:对象.属性名=属性值
//向obj中添加一个 name属性
obj.name ="猪八戒";
//向obj中添加一个 gender属性
obj.gender ="男";
//向obj中添加一个 age属性
obj.age = 18;
  1. 读取对象中的属性
    语法:对象.属性名
console.log(obj.name);// 猪八戒

如果读取对象中没有的属性,不会报错,会返回undefined

  1. 修改对象中的属性
    语法:对象.属性名=新值
obj.name = "沙和尚"
  1. 删除对象中的属性
    语法:delete 对象.属性名
delete obj.name;
3.属性名和属性值
  1. 属性名
    除了通过对象.属性名 = 属性值这种方式给对象添加属性之外,还可以通过另一个方法:
    语法:对象[“属性名”] = 属性值
    这种写法的属性名可以是任何字符组成,甚至可以是变量
var obj = new Object();
obj["123"] = 789;
obj["wenhao"] = "你好呀!";
var n = "wenhao"; // 让变量n等于属性名
console.log(obj["123"]); // 789
console.log(obj[n]); // 通过读取变量n得到属性名“wenhao”,从而得到属性值“你好呀!”
  1. 属性值
    JS对象的属性值可以是任意的数据类型,甚至可以是对象
var obj = new Object();
obj.test = null;
obj.test = undefined;
var obj2 = new Object();
obj2.name = "宋江";
obj.test2 = obj2;
console.log(obj.test2);
console.log(obj.test2.name); // 读取ogj2中的属性

在这里插入图片描述

4.基本数据类型和引用数据类型

变量b等于变量a,改变a的值,b的值不会变

var a = 123;
var b = a;
a++
console.log(a); // 124
console.log(b); // 123

原因:
JS中的变量都是保存到栈内存中的,基本数据类型的值直接在栈内存中存储,值与值之间是独立存在,修改一个变量不会影响其他的变量

在这里插入图片描述
对象obj2等于obj,改变obj的name,obj2的name也会随之改变

var obj = new Object();
obj.name = 'swk';
var obj2 = obj;
obj.name = 'zbj';
console.log(obj.name);  // zbj
console.log(obj2.name); //zbj

原因:
对象是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存的是对象的内存地址(对象的引用),如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响

在这里插入图片描述
对象obj2等于obj,令obj2等于null,obj没有变成null

var obj = new Object();
obj.name = 'swk';
var obj2 = obj;
obj2 = null;
console.log(obj);  // Object
console.log(obj2); //null

原因:obj2=null就是让它的指向为null,此时不再和obj引用同一个地址
在这里插入图片描述
obj3.name和obj4.name都是‘shs’,但是不相等

var obj3 = new Object();
var obj4 = new Object();
obj3.name = 'shs';
obj4.name = 'shs';
console.log(obj3.name == obj4.name); // false

原因:
当比较两个基本数据类型的值时,就是比较值。
而比较两个引用数据类型时,它是比较的对象的内存地址,
如果两个对象是一摸一样的,但是地址不同,它也会返回false

在这里插入图片描述

5.对象的字面量

新建对象的第二种方法:

var obj = {
    
    };

优点:

  1. 更加方便简洁
  2. 创建对象是,可以直接指定属性名和属性值。
var obj = {
    
    
	name:'swk',
	age:24
}
6.内建对象
(1).数组

数组也是对象
数组和我们普通对象功能类似,也是用来存储一些值,不同的是普通对象是使用字符串作为属性名的,而数组是使用数字来作为索引操作元素
索引: 从0开始的整数就是索引。
为什么使用数组?
数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据。

var arr = new Array(); // 创建一个数组对象
var arr2 = []; // 使用字面量创建一个数组对象
console.log(arr); // []
console.log(typeof arr); // object

向数组中添加元素
语法:数组名[索引] = 值

arr[0] = 10; // 向数组中索引为0的地方添加值10
arr[1] = 20;
console.log(arr[3]); // 读取不存在的索引,不会报错,会返回undefined

可以使用length属性来获取数组的长度(元素的个数)
语法:数组.length

arr[0] = 10; 
arr[1] = 20;
console.log(arr.length); //2
console.log(arr); // [10, 20]
arr[10] = 20;
console.log(arr.length); //11
console.log(arr); // [10, 20, , , , , , , ,20]未赋值的地方默认为空

对于连续的数组,使用length可以获取到数组的长度(元素的个数),对于非连续的数组,使用length会获取到数组的最大的索引+1

修改length

arr[0] = 10; 
arr[1] = 20;
arr[2] = 30;
arr[3] = 40;
arr.length = 10 // 设置长度为10
console.log(arr); // [10, 20, 30, 40, , , , , ,]
arr.length = 2 // 设置长度为2
console.log(arr); // [10,20] 后面的值被截断了

数组里的元素可以是任意类型的

var arr = ['Hello',true,123,null,undefined];
console.log(arr);

可以放对象

var obj = {
    
    name:'swk'}
var arr = [obj];
console.log(arr);
console.log(arr[0].name);

可以放函数

var arr = [function fun(){
    
    console.log('我是一个函数')}];
console.log(arr);
console.log(arr[0]());

可以放数组

var arr = [[1,2,3],[4,5,6]];
console.log(arr);

数组的方法:

  • pop: 可以删除数组的最后一个元素,并将该元素作为返回值返回
  • push: 向数组的末尾插入一个或多个元素,并将数组新长度作为返回值返回
  • unshift: 向数组开头插入一个或多个元素,并将数组新长度作为返回值返回
  • shift: 可以删除数组的第一个元素,并将该元素作为返回值返回
  • forEach()
    forEach()是一个回调函数。
    回调函数:有我们创建,但是不由我们调用,浏览器自己调用的函数
    forEach()有四个参数:
    第一个参数(例子中的value),就是当前正在遍历的
    第二个参数(例子中的index),就是当前正在遍历的元素的索引
    第三个参数(例子中的arr),就是正在遍历的数组
    第四个参数(例子中的arr2),this(代表你想使用的对象)
var arr = ['孙悟空','猪八戒','沙和尚','唐僧','牛魔王'];
var arr2 = ['123'];
arr.forEach(function(value,index,arr) {
    
    
	console.log('value = '+value);
	console.log('index= '+index);
	console.log('arr= '+arr);
	console.log('a=',this[0]); // 此时this指向arr2
},arr2)

在这里插入图片描述

  • slice
    可以用来从数组提取指定元素
    该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回
    参数:
    1.截取开始的位置的索引,包含开始索引
    2.截取结束的位置的索引,不包含结束索引,第二个参数可以省略不写
  • splice
    使用splice()会影响到原数组,会将指定元素从原数组中删除
    并将被删除的元素作为返回值返回
    参数:
    1.表示开始位置的索引
    2.表示删除的数量
    3及之后的多个参数.表示替换进来的元素,这些元素将会自动插入到开始位置索引前边
  • concat
    用于连接两个或多个数组,或者元素
var arr = [1,2,3];
var arr2 = [4,5,6];
var arr3 = [7,8,9];
var result = arr.concat(arr2); // 连接两个数组
var result2 = arr.concat(arr2,arr3); // 连接多个数组
var result3 = arr.concat(arr2,arr3,'a','b','c'); // 还能连接多个元素
console.log(result);
console.log(result2);
console.log(result3);
console.log('arr = '+arr+' || arr2 = '+arr2+' || arr3 = '+arr3); //concat不会影响原数组

在这里插入图片描述

  • join
    将数组转换为字符串
var arr = [1,2,3];
var result = arr.join(); // 默认使用逗号(,)来作为连接符
var result2 = arr.join('@@@'); // 使用@@@作为连接符,
console.log(result);
console.log(typeof result); // 类型是String
console.log(result2);
console.log('arr = '+arr); // 不会影响原数组

在这里插入图片描述

  • reverse
    将数组翻转
var arr = [1,2,3,4,5];
var result = arr.reverse();
console.log(result); // 将数组头尾翻转
console.log(arr); // 原数组会被影响

在这里插入图片描述

  • sort
    根据Unicode码进行排序
var arr = ['b','d','a','c','f','e'];
var result = arr.sort();
console.log(result); // 根据Unicode重新排序
console.log(arr); // 会影响原数组

在这里插入图片描述

// 要求升序排列
var arr = [5,4,9,2,7,11];
var result = arr.sort();
console.log(result); // 纯数字也会根据Unicode重新排序,但是有两位数和一位数时,只会比较第一位数,会出现问题
var result2 = arr.sort(function(a,b) {
    
     // 可以加一个回调函数,修改sort()比较数字时的bug
	if (a>b) {
    
    
		return 1; // 返回大于0的数,a和b会交换位置
	} else if (a<b) {
    
    
		return -1; // 返回小于0的数,a和b不会交换位置
	} else {
    
    
		return 0; // 返回等于0的数,a和b不会交换位置
	}
	// 更简便的方法
	// return a - b; // 升序排列
	// return b - a; // 降序排列
});
console.log(result2); // 加上回调函数后的结果

在这里插入图片描述

(2).函数的方法call()和apply()
  • 这两个方法都是函数对象的方法,需要通过函数对象来调用
  • 当对函数调用call()和apply()都会调用函数执行
  • 在调用call()和apply()可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this
  • call()方法可以将实参在对象之后依次传递
  • apply()方法需要将实参封装到一个数组中统一传递
function fun(a,b) {
    
    
	console.log('我是fun函数');
	console.log('我的名字是'+this.name);
	console.log('a= '+a+' , b= '+b);  
}
var obj = {
    
    name:'swk'};
var obj2 = {
    
    name:'zbj'};
fun(); // 我是fun函数
fun.call(); // 可以调用fun函数
fun.apply(); // 可以调用fun函数
fun.call(obj,2,3); // this指向obj,2和3是实参
fun.apply(obj2,[4,5]); // this指向obj2,4和5是实参,用数组的形式传入

在这里插入图片描述
this的情况:

  1. 以函数形式调用时,this永远都是window
  2. 以方法的形式调用时,this是调用方法的对象
  3. 以构造函数的形式调用时,this是新创建的那个对象
  4. 使用call()和apply()调用时,this是指定的那个对象
(3).arguments

在调用函数时,浏览器每次都会传递进两个隐含的参数:

1.函数的上下文对象this
2.封装实参的对象arguments

  • arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
  • 在调用函数时,我们所传递的实参都会在arguments中保存
  • arguments.length可以用来获取实参的长度
  • 我们即使不定义形参,也可以通过arguments来使用实参
  • 它里边有一个属性叫做callee,这个属性对应一个函数对象,就是当前正在指向的函数的对象
function fun(){
    
    
	console.log('是否是数组:'+Array.isArray(arguments));
	console.log('实参的长度:'+arguments.length);
	console.log('第一个实参:'+arguments[0]);
	console.log(arguments.callee == fun);
}
fun('Hello',1,2,true);

在这里插入图片描述

(4). Date对象

getTime()

  • 获取当前日期对象的时间戳
  • 时间戳,指的是从格林威治标准时间的1979年1月1日,0时0分0秒到当前日期所花费的毫秒数(1秒=1000毫秒)
  • 计算机底层在保存时间时使用都是时间戳
  • 获取当前时间的时间戳:
var time = Date.now();
(5). Math对象

Math对象不是构造函数,所以使用是不需要创建一个新的对象,直接使用其属性和方法即可
Math的属性:

console.log(Math.PI);

Math的方法:

  • ceil:向上取整
  • floor:向下取整
  • round:四舍五入取整
  • random:可以用来生成一个0~1的随机数

生成X-Y的随机数
Math.round(Math.random * (y-x)+x)

  • max:获取多个数中的最大值
  • min:获取多个数中的最小值
  • Math.pow(x,y):返回x的y次幂
  • Math.sqrt(x):返回x进行开方后的值
(6).包装类

在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象

String()

  • 可以将基本数据类型字符串转换为String对象

Number()

  • 可以将基本数据类型的数字转换为Number对象

Boolean()

  • 可以将基本数据类型的布尔值转换为Boolean对象但是注意:我们在实际应用中不会使用基本数据类型的对象,

如果使用基本数据类型的对象,在做一些比较的可能会带来一些不可预期的结果,既然我们不使用,那么包装类存在的意义是什么?

方法和属性只能添加给对象,不能添加给基本数据类型

var a = 123;
a = a.toString(); // 基本类型没有方法和属性,那么是怎么调用toString()的呢?
console.log(a);  // 123
console.log(typeof a);  //String

当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后在调用对象的属性和方法调用完以后,再将其转换为基本数据类型

(7).正则表达式
  • search()
    可以搜索字符串中是否含有指定内容,如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回-1
    它可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串
    search()只会查找第一个,即使设置全局匹配也没用
str = 'hello abc hello aec afc';
var result = str.search(/a[bef]c/); // 查找abc或aec或afc
var result2 = str.search(/a[bef]c/g); // g代表全局匹配搜索
console.log(result);  //返回第一个匹配的字符的位置
console.log(result2);  // search()只会查找第一个,即使设置全局匹配也没用
  • match()
    可以根据正则表达式,从一个字符串中将符合条件的内容提取出来
    默认情况下我们的match只会找到第一个符合要求的内容,找到以后就停止检索
    我们可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容
    match()会将匹配到的内容封装到一个数组中返回,即使只查询到一个结果
str = "1a2b3c4h5e6l7l8B9";
var result = str.match(/[A-z]/); // 查找字母,找到一个后就返回
var result2 = str.match(/[A-z]/g); // g代表全局匹配搜索
console.log(result);  // 不使用g全局匹配搜索只返回一个值
console.log(result2); // 使用全局搜索会返回所有搜索到的值
console.log(Array.isArray(result)); // 判断是否是数组

在这里插入图片描述

  • replace()
    可以将字符串中指定内容举换为新的内容
    参数:
    1.被替换的内容,可以接受一个正则表达式作为参数
    2.新的内容
    默认只会替换第一个
str = "1a2b3c4h5e6l7l8B9";
var result = str.replace(/[A-z]/,'@-@'); // 将第一个字母替换为@-@
var result2 = str.replace(/[a-z]/ig,'$'); // 将全局字母替换为$
console.log(result);  
console.log(result2); 

在这里插入图片描述

  • split()
    可以将一个字符串拆分为一个数组
    方法中可以传递一个正则表达式作为参数,这样方法将会根据正则表达式去拆分字符串,这个方法即使不指定全局匹配,也会全都拆分
str = "1a2b3c4h5e6l7l8B9";
var result = str.split(/[A-z]/); // 根据字母拆分
var result2 = str.split(/[a-z]/ig); // g代表全局匹配搜索,i表示忽略大小写,可以两种模式一起用
console.log(result);  // 不使用g全局匹配搜索也会自动全部拆分
console.log(result2); 

在这里插入图片描述

六.函数

1.创建函数
  1. 因为函数也是对象,可以使用new的方式创建一个函数对象(但是开发中不推荐,麻烦)
var fun  = new Function("console.log('我是new出来的函数对象');");
fun();
  1. function 函数名([参数1,参数2,参数3…]) {}
function fun2(){
    
    
	console.log("我是直接创建的函数!");
}
fun2();
  1. 给变量赋值一个匿名函数
var fun3 = function(){
    
    
	console.log('我是赋值给变量的匿名函数!');
};
fun3();

拓展:
立即执行函数:函数定义完,立即被调用,这种函数称为立即执行函数。立即执行函数往往只会执行一次。因为没有函数名称,后面不能再次调用

(function(){
    
    
	console.log('我是立即执行函数');
})(); // 后面的括号表示掉这个匿名函数
2.形参和实参

创建函数时的括号的值是形式参数(形参),调用函数传入的值是实际参数(实参)

function fun(a,b){
    
    } // a和b是形参
fun(1,2); // 1和2是实参

声明形参相当于在函数内部定义了相同名字的两个变量,但是并不赋值

  1. 调用函数时解析器不会检查实参的类型
// 求两数的和
function sum (a,b){
    
    
	console.log('他们的和是:'+(a+b));
}
sum(1,2); // 3
sum(true,false); //1
sum(123,'Hello'); //123Hello

所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查,因为函数的实参可以是任意的数据类型

  1. 调用函数时,解析器也不会检查实参的数量
// 求两数的和
function sum (a,b){
    
    
	console.log('他们的和是:'+(a+b));
}
sum(1,2,3,4); //3
sum(1); // 1+undefined=NaN

多余实参不会被赋值
如里实参的数量少于形参的数量,则没有对应实参的形参将是undefined

3.返回值

函数可以通过return来返回值

// 求两数的和
function sum (a,b){
    
    
	return a+b;
	console.log('hahahaha'); //return后的语句不会执行
}
var result = sum(1,2); 
console.log(result);

return后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果
在函数中return后的语句都不会执行,如果return语句后不跟任何值就相当于返回一个undefined,如果函数中不写return,则也会返回undefined。

4.对象和函数的关系

对象的属性也可以是函数

var obj = new Object();
obj.name = 'swk';
obj.say = function(){
    
    
	console.log('Hello');
}
console.log(obj.say);
console.log(obj.say());

函数可以成为对象的属性
如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法
调用这个函数就说调用对象的方法(method)
但是方法和函数只是名称上的区别没有其他的区别

5.枚举对象属性
var obj = {
    
    
	name:'zbj',
	age:19,
	gender:'男',
	address:'花果山'
};
for(var n in obj){
    
    
	console.log('属性名:'+n);
	console.log('属性值:'+obj[n]);
}
6.函数和变量的提前声明
  1. 变量的声明提前
    使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值),
    但是如果声明变量时不适用var关键字,则变量不会被声明提前
console.log(a); //不会报错,但是输出undefined
console.log(b); // 报错
var a = 123;
b = 123
  1. 函数的声明提前
    使用函数声明形式创建的函数,function 函数名(){}
    它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数,
    使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用
fun();  //可以执行
fun2(); // 报错
function fun(){
    
     //函数声明,会被提前创建
	console.log('我是函数声明形式创建的函数');
}
var fun2 = function(){
    
     // 函数表达式,fun2会被声明,他的值为undefined,但是函数不会被创建
	console.log('我是函数表达式创建的函数');
}
7.作用域

作用域指一个变量的作用的范围

在JS中一共有两种作用域:

  1. 全局作用域
    直接编写在script标签中的JS代码,都在全局作用域中,全局作用域在页面打开时创建,在页面关闭时销毁。
    在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建,我们可以直接使用。
    在全局作用域中:
    创建的变量都会作为window对象的属性保存创建的,函数都会作为window对象的方法保存,全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到
console.log(window); // window是个对象
var a = 123;
console.log(window.a); // 全局变量a也是window的属性
function fun(){
    
    
	console.log('我是window的方法');
}
window.fun(); // 创建的函数fun也是window的方法

在这里插入图片描述
2. 函数作用域
调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁,每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的。
在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量。

var a = 123;
function fun() {
    
    
	console.log(a); // 123
	var b = 456;
}
fun();
console.log(b); //报错 b is not defined

当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有则向上一级作用域中寻找,直到找到全局作用域,如果全局作用域中依然没有找到,则会报错ReferenceError。

var a = 123;
var b = 456;
function fun() {
    
    
	var a = 'hello';
	var b = 789;
	console.log(a); // hello
	function fun2() {
    
    
		console.log(b); //789
		console.log(c); //ReferenceError: c is not defined
	}
	fun2();
}
fun();

在函数中要访问全局变量可以使用window对象
创建变量时,没有使用var关键字,则创建的变量会成为全局变量

注意:使用 delete 可以删除对象的属性,同理也就能删除 window 的属性,如果不使用 var 创建变量(属性window对象的属性)可以使用 delete 删除,但是使用 var 创建的变量(也是window对象的属性)不能使用 delete 删除,因为使用 var 创建的变量,他的 configurable 属性默认为false,所以不能使用 delete 删除。可以通过Object.getOwnPropertyDescriptor(对象,‘属性名’)来查看改对象属性的“属性内部对象”

var a = 10;
function fun() {
    
    
 	var a = 123;
 	console.log(window.a); // a = 10  通过window,可以访问到被局部变量覆盖后的全局变量
 	b = 456; // 没有使用var 创建变量,所以b是全局变量
 	a = 789; 
 	console.log(a); // 789
}
fun();
console.log(b); // 456
console.log(a); // 10

在函数作用域也有声明提前的特性
使用var关键字声明的变量,会在函数中所有的代码执行之前被声明,函数声明也会在函数中所有的代码执行之前执行

var a = 111;
function fun() {
    
    
	fun2(); //提前声明和赋值,输出123
	console.log(a); //变量提前声明,但是没有赋值,所以a=undefined,即使有全局变量a,a还是undefined
	var a = 123;
	function fun2() {
    
    
		console.log('123');
	}
}
fun();
8.this

解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this, this指向的是一个对象,这个对象我们称为函数执行的上下文对象。
根据函数的调用方式的不同,this会指向不同的对象
1. 以函数的形式调用时,this永远都是window
2. 以方法的形式调用时,this就是调用方法的那个对象

function fun() {
    
    
	console.log(this); //this是一个隐含传入的参数
	console.log(this.name);
}
var obj = {
    
    
	name:'swk',
	say:fun
}
var obj2 = {
    
    
	name:'zbj',
	say:fun
}
var name = 'shs';
obj.say(); // swk,以方法的形式调用时,this就是调用方法的那个对象
obj2.say(); // zbj
fun(); //shs, 以函数的形式调用时,this永远都是window
console.log(obj.say == fun);  //true代表两者是同一个地址的函数
console.log(obj2.say == fun);
console.log(obj.say == obj2.say);

在这里插入图片描述

9.构造函数

构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写

function Person(name,age,gender){
    
     //首字母大写
	this.name = name;
	this.age = age;
	this.gender = gender;
	this.say = function(){
    
    
		console.log('你好!');
	}
}
var per = new Person('swk',18,'男'); // 使用new是调用构造函数Person,不使用new则是调用普通函数Person
var per1 = new Person('zbj',28,'男'); //我是Person类的一个实例
var per2 = new Person('shs',38,'男'); //我也是Person类的一个实例

function Dog(name,age){
    
    
	this.name = name;
	this.age = age;
	this.say = function(){
    
    
		console.log('汪汪~~');
	}
}
var dog = new Dog('柴犬',1); //我是Dog类的一个实例
console.log(per); // Person {name: "swk", age: 18, gender: "男", say: ƒ}
console.log(dog); // Dog {name: "柴犬", age: 1, say: ƒ}

构造函数和普通函数的区别就是调用方式的不同
普通函数是直接调用,而构造函数需要使用new关键字来调用
构造函数的执行流程:
1.立刻创建一个新的对象
2.将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
3.逐行执行函数中的代码
4.将新建的对象作为返回值返回

使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象,称为是该类的实例

this的情况:
1.当以函数的形式调用时,this是window
2.当以方法的形式调用时,谁调用方法this就是谁
3.当以构造函数的形式调用时,this就是新创建的那个对象

console.log(per instanceof Person); //true
console.log(dog instanceof Person); //false
console.log(dog instanceof Dog); //true
console.log(dog instanceof Object); //true
console.log(Dog instanceof Object); //true

使用 instanceof 可以检查一个对象是否是一个类的实例,语法:对象 instanceof 构造函数
如果是,则返回 true,否则返回 false
Object是所有类的父类,所有对象都是Object的后代,所有对象和类做 instanceof 查询时都会返回 true

10.原型prototype

我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype

这个属性对应着一个对象,这个对象就是我们所谓的原型对象

  • 如果函数作为普通函数调用prototype没有任何作用

  • 当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,见下方

function MyClass() {
    
    
}
function Person() {
    
    
}
console.log(MyClass.prototype); // 每个构造函数都有prototype
console.log(Person.prototype);
console.log(Person.prototype == MyClass.prototype); // false,每个构造函数的prototype都不同
  • 指向该构造函数的原型对象,我们可以通过__proto__来访问该属性,见下方
function MyClass() {
    
    
}
var mc = new MyClass();
console.log(mc.__proto__);
console.log(mc.__proto__ == MyClass.prototype); // true

在这里插入图片描述

  • 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

  • 当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。见下方

function MyClass() {
    
    
}
MyClass.prototype.a = 123;
var mc = new MyClass();
console.log(mc.a); // 123
mc.a = 456;
console.log(mc.a); //456
  • 以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了
function Person(name,age,gender) {
    
    
	this.name= name;
	this.age = age;
	this.gender = gender;
}
Person.prototype.say = function() {
    
    
	console.log('Hello,我是:' + this.name);
}
var per = new Person('swk',18,'男');
var per2 = new Person('zbj',28,'男');
per.say();
per2.say();

原型链
原型对象也是对象,所以它也有原型
当我们使用一个对象的属性或方法时,会现在自身中寻找,自身中如果有,则直接使用,
如果没有则去原型对象中寻找,如果原型对象中有,则使用,
如果没有则去原型的原型中寻找,直到找到Object对象的原型。
Object对象的原型没有原型,如果在Object中依然没有找到,则返回undefined

function MyClass() {
    
    
}
var mc = new MyClass();
// 在对象的原型中查找hasOwnProperty方法
console.log(mc.__proto__.hasOwnProperty('hasOwnProperty')); //false
// 在对象的原型的原型中查找hasOwnProperty方法
console.log(mc.__proto__.__proto__.hasOwnProperty('hasOwnProperty')); // true
console.log(mc.__proto__.__proto__); // 对象原型的原型
console.log(mc.__proto__.__proto__.__proto__); // null 没有对象原型的原型的原型
11.垃圾回收(GC)
  • 就像人生活的时间长了会产生垃圾一样,程序运行过程中也会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢。所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾。
  • 当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
  • 在]S中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作。
  • 我们需要做的只是要将不再使用的对象设置null即可
    在这里插入图片描述

七、DOM

1.什么是DOM?
  • DOM,全称是 Document Object Model 文档对象模型
  • JS通过DOM来对HTML文档进行操作
  • 文档 Document
    文档表示的是整个的HTML页面
  • 对象 Object
    对象表示将网页中的每一个部分都转换为一个对象
  • 模型 Model
    使用模型来 表示对象之间的关系,这样方便我们获取对象
    在这里插入图片描述
2.节点
  • 节点Node,是构成我们网页的最基本的组成部分,网页中的每一个部分都可以称为是一个节点。比如: html标签、属性、文本、注释、整个文档等都是一个节点。
  • 虽然都是节点,但是实际上他们的具体类型是不同的。比如∶标签我们称为元素节点、属性称为属性节点、文本称为文本节点、文档称为文档节点
  • 节点的类型不同,属性和方法也都不尽相同。在这里插入图片描述
    在这里插入图片描述
文档节点 Document
  • 文档节点document,代表的是整个HTML文档,网页中的所有节点都是它的子节点。
  • document对象作为window对象的属性存在的,我们不用获取可以直接使用。
  • 通过该对象我们可以在整个文档访问内查找节点对象,并可以通过该对象创建各种节点对象。
元素节点 Element
  • HTML中的各种标签都是元素节点,这也是我们最常用的一个节点。
  • 浏览器会将页面中所有的标签都转换为一个元素节点,我们可以通过document的方法来获取元素节点。比如:
    document.getElementByld()
    根据id属性值获取一个元素节点对象。
文本节点 Text
  • 文本节点表示的是HTML标签以外的文本内容,任意非HTML的文本都是文本节点。它包括可以字面解释的纯文本内容。
  • 文本节点一般是作为元素节点的子节点存在的。
  • 获取文本节点时,一般先要获取元素节点。在通过元素节点获取文本节点。例如∶
    元素节点.firstChild;
    获取元素节点的第一个子节点,一般为文本节点
属性节点 Arrt
  • 属性节点表示的是标签中的一个一个的属性,这里要注意的是属性节点并非是元素节点的子节点,而是元素节点的一部分。
  • 可以通过元素节点来获取指定的属性节点。例如∶
    元素节点.getAttributeNode(“属性名”)
    注意:我们一般不使用属性节点。
3.事件
  • 事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。
  • JavaScript 与HTML之间的交互是通过事件实现的。
  • 对于Web应用来说,有下面这些代表性的事件:点击某个元素、将鼠标移动至某个元素上方、按下键盘上某个键,等等。
事件对象

当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数,在事件对象中封装了当前事件相关的一切信息,比如:鼠标的坐标网键盘哪个按键被按下鼠标滚轮滚动的方向。。。

btn.onclick = function(event) {
    
    
	console.log('我是事件对象:' + event);
}
事件的传播

事件的冒泡
HTML显示如下:三个div一个套着一个
在这里插入图片描述
JS代码如下:

window.onload = function() {
    
    
	var one = document.getElementById('one');
	var two = document.getElementById('two');
	var three = document.getElementById('three');
	one.onclick = function() {
    
    
	console.log('我是青色方块!')
	}
	two.onclick = function() {
    
    
		console.log('我是绿色方块!')
	}
	three.onclick = function() {
    
    
		console.log('我是黄色方块!')
	}
}

在这里插入图片描述
我点击黄色方块,按道理只会触发一个事件,但是,三个事件都触发了。这个情况就是事件的冒泡。
点击目标元素,会触发该元素的祖先元素的相同事件

事件的传播

  1. 捕获阶段
    在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
  2. 目标阶段
    事件捕获到目标元素,捕获结束开始在目标元素上触发事件
  3. 冒泡阶段
    事件从目标元素向他的祖先元素传递,依次触发祖先元素上的事件
    在这里插入图片描述
    如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true,—般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false。
    IE8及以下的浏览器中没有捕获阶段
// addEventListener()函数
addEventListener(eventStr, callback, false){
    
    
// eventStr:事件的名称,不带on,例如click
// callback:回调函数
// false:如果为false,则捕获阶段不会触发事件,如果为true,捕获阶段会触发事件
}
4.BOM对象

BOM:浏览器对象模型
BOM可以使我们通过JS来操作浏览器,在BOM中为我们提供了一组对象,用来完成对浏览器的操作-BOM对象

  • Window
    代表的是整个浏览器的窗口,同时window也是网页中的全局对象
  • Navigator
    代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器
  • Location
    代表当前浏览器的地址栏信息,通过Location可以获取地址栏信息,或者操作浏览器跳转页面
  • History
    代表浏览器的历史记录,可以通过该对象来操作浏览器的历史记录
    由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页而且该操作只在当次访问时有效
  • Screen
    代表用户的屏幕的信息,通过该对象可以获取到用户的显示器的相关的信息
console.log(window);
console.log(navigator);
console.log(Location);
console.log(history);
console.log(screen);

在这里插入图片描述
这些BOM对象在浏览器中都是作为window对象的属性保存的,可以通过window对象来使用,也可以直接使用

5.JSON

JavaScript Object Notation JS对象表示法

  • JSON 就是一个特殊格式的字符串,这个字符串可以被任意的语言所识别,并且可以转换为任意语言中的对象,JSON 在开发中主要用来数据的交互
  • JSON 和 JavaScript 对象的格式一样,只不过 JSON 字符串中的属性名必须加双引号,其他的和 JavaScript 语法一致
JSON —> JS对象

JSON.parse()

  • 可以将以JSON字符串转换为JS对象
  • 它需要一个JSON字符串作为参数,会将该字符串转换为JS对象
var json = '{"name":"swk","age":18}'; // JSON格式的数据
var obj = JSON.parse(json); // 将JSON格式数据转为JS对象
console.log(typeof json); // JSON的类型:string
console.log(typeof obj); // 转换后的类型:object
JS对象 —> JSON

JSON.stringify()

  • 可以将一个JS对象转换为JSON字符串
  • 需要一个JS对象作为参数,会返回一个JSON格式的字符串
var obj = {
    
    name:"zbj", age:18, gender:"男"}; // JSON格式的数据
var json = JSON.stringify(obj); // 将JSON格式数据转为JS对象
console.log(typeof json); // 转换后的类型:string
console.log(typeof obj); // JS对象类型:object

八、最后

最后,推荐阮一峰老师的《JavaScript 语言入门教程》

猜你喜欢

转载自blog.csdn.net/weixin_46683645/article/details/116067284