JavaScript基础

2023-11-19

编程之修,重在积累,而非资质。资质虽然重要,可是后天的努力更不可缺少。

直接量

编程世界中的直接量,就是表面上可以见到的数据值。常见的直接量有数字、小数、字符串。

  1. 字符串的出现必然带着双引号(单引号也可以),被很好地包裹住,而数字则是光秃秃的,如10或者20,没有双引号。

    "hello world";
    'hello world';
    10;
    

变量的声明

直接量只是昙花一现,但是如果能用var定义一个变量,再将它指向那个直接量,就能有保存数据的妙用了。

  1. 定义了一个变量。
    var a;
    
  2. 变量a指向了这个字符串。
    var a;
    a = "hello world";
    
  3. 先定义变量,然后指向一个字符串,这种操作分成了两步,直接合并为一句更好。
    var a = "hello world";
    

数据类型

在JavaScript中,数据可分为两类,分别为原生数据类型(primitive type)和对象数据类型(object type)。

  1. 原生数据类型包括数字、字符串、布尔值,还有两个特殊的类型:null和undefined。
    var num = 6;
    
  2. 布尔值,它是一种只有true和false两种状态的类型。
  3. null和undefined,从用法上来看,null和undefined都代表了直接量的空缺,如果一个变量指向了其中任何一个,都代表false的含义,也表示没有或空的概念。而从根本意义上讲,undefined要比null更加严重一点,代表本不应该出现的错误,比如我刚才定义了一个变量a,但是我没有把任何直接量赋给它,那么a就默认指向了undefined;而null不同,有的时候,我需要给某些变量赋值null,以达到清空的目的。

拓展阅读

JavaScript包括直接量和变量。首先说直接量,什么是直接量呢?在JavaScript的世界里,直接量包含数值(如10/20)、逻辑值(true/false)、字符串(如“nihao”)、null、undefined、对象和函数。

  1. 其中,函数也称方法,对象和函数会在之后的章节中慢慢介绍。你暂时可以认为对象是存放数据的一个容器,而函数是应用程序处理某一系列逻辑的一个过程设计
  2. null是一个特殊的关键字,表示没有值;null也是一个原始值,因为JavaScript是大小写敏感的,所以null和Null、NULL或者其他变量是有区别的。
  3. undefined是一个顶级属性,它代表某一个变量未定义。同样,undefined也是一个原始值。

说完直接量,再来说变量。所谓变量,就是指向了某个直接量或者其他变量的“钥匙”。比方说,把一个直接量true比作一扇门,然后定义一个变量flag,最后通过赋值运算符“=”将这个true赋值给flag,这样就完成了一个变量的绑定。从此以后,你在别处使用变量flag,也就相当于使用了直接量true。简单来说,就是这么回事。

对象数据类型

所谓对象数据类型,是一种复合型的数据类型,它可以把多个数据放到一起,就好像一个篮子,这个篮子里面的每一个数据都可以看作是一个单元,它们都有自己的名字和值。

  1. 这是创建对象的一种方式,也是最常用的方式。创建对象以后,就相当于开辟了一块内存,对象包含若干数据,每个数据都有自己的名字和值。对象好比是一个容器,现在我要在这个容器里面放一个数据。
    	var container = {
    		caoyao : "草药"
    	};
    //在这个例子中,caoyao叫作键,草药叫作值,它是一种键值对的形式。
    //一个键对应一个值,一个键和一个值就凑成了一对,键和值中间用冒号。
    
  2. 如果你想要在一个对象里面添加新的数据,则只需要添加一个逗号,然后写上新的键值对就行了。
    var container = {
    		caoyao : "草药",
    		feijian : "乌木剑"
    	};
    
  3. 上面演示的方式是在创建对象的时候立刻在对象里面设置键值对。其实还有其他办法,那就是在对象创建之后,在外面对这个对象的变量进行操作。
    	var container = {};
    	container.caoyao = "草药",
    	container.feijian = "乌木剑"
    //container.caoyao中的点(.)就是对象访问属性的意思,
    // 正因为caoyao是container的属性,所以container才可以用点(.)。
    // 对象包含若干数据,每个数据都是一个键值对,这些数据也叫作对象的属性。
    // 那么键值对中的键就是属性名称,键值对中的值就是属性值。
    
  4. 如果我直接访问一个根本不存在的属性danyao,
    	var container = {};
    	container.caoyao = "草药",
    	container.feijian = "乌木剑"
    	console.log(container.danyao); //注意danyao属性不存在。 结果是undefined。
    //danyao这个属性不存在于container对象中,因此它是未定义的,得到的结果就是undefined!
    

对象的取值

  1. 对象可以通过一个点号(.)访问其中的某一个数据
    var container = {};
    container.caoyao = "草药",
    container.feijian = "乌木剑"
    console.log(container.caoyao);
    
  2. 如果遇到这种情况,即事先不知道调用的属性叫什么名字,那么该如何用一个变量定义属性呢。
    	var container = {};
    	container.caoyao = "草药",
    	container.feijian = "乌木剑"
    	var prop = "caoyao";
    	console.log(container[prop]);
    //对象不仅可以用点号(.)访问它的一个属性,也可以用中括号([])。
    //如果用中括号,里面就允许再写一个变量。当然了,写字符串也是可以的。
    //console.log(container["caoyao"]);
    
    /*如果事先属性的名称未知,或者调用的属性是动态变化的,就不能使用点号了。
    使用中括号可以最大程度地提升对象调用属性的灵活度!*/
    

循环遍历的奥妙

如果你希望一遍又一遍地运行相同的代码,并且每次的值都不同,那么使用循环是很方便的。

  1. 使用for循环
    for(var i=0; i<10; i++){
    	console.log(i);
    }
    
  2. 使用while循环
    var i = 0;
    while(i<10){
    	console.log(i);
    	i++;
    }
    

i++是自增运算符,表示把当前的变量自增一个单位。而++i和i++是有区别的,前者代表先自增一个单位,再运算;后者相反,表示先运算,再自增一个单位。但是由于这段代码中的i++占单独一行,没有对i进行使用,所以不管是++i还是i++,只要这句话执行完毕,i的值都会自增。

while循环和for循环除了语法还有什么区别

for循环中有一个小括号。小括号里面有3个表达式,分别为“vari=0”,“i<10”还有“i++”。

  1. 第1个语句是在循环开始之前执行的,“var i=0”的意思是定义了一个变量i,是整数,初始值为0。
  2. 第2个语句是“i<10”,表示进入循环体的条件。循环体就是那个用大括号({})扩起来的部分。
  3. 最后一个语句“i++”,这个语句是在刚才我们所说的大括号里面的代码被全部执行之后才会被执行的。一般来说,上面这段语句里面的代码可以控制循环变量i自增一个单位或者自减一个单位。

调试代码
在这里插入图片描述

  • 语句1执行完毕后就自然会执行语句2了,也就是“i<10”这句话,这就好比是一个if判断。
    var i = 0;
    if(i<10){
    	console.log(i);
    }
    

while循环只是在语法上有所不同,其作用和for循环是一样的。

小结

下面是for循环的语法。
在这里插入图片描述
while循环会在指定条件为真时循环执行代码块。
在这里插入图片描述

对象内容的遍历

  • typeof关键字,可以得到变量的类型。
    	var a = "123";
    	var fun = function{};
    	console.log(typeof(a));
    	console.log(typeof(fun));
    /*a是一个字符串,所以typeof出来就是string;
    fun是一个函数,所以typeof出来就是function。*/
    
  1. 如何遍历一个对象。首先,新建一个简单的JavaScript对象。
    var fun = {
    	name = "叶小凡";
    	age = 16;
    	eat:function(){
    	container.log("KFC");
    	}
    };
    
  2. 然后使用for循环进行遍历。
    for(var i in fun){
    	container.log(i + " = " + fun[i]);
    }
    //既然有了属性名称,那么对象可以用点(.)的方式直接获取属性的值。
    //当然,用中括号([])也是可以的。
    
  3. 运行结果如下。
    在这里插入图片描述

因为遍历出来的属性名称是不确定的,而是用一个i变量指代,既然是变量,自然不可以用点号。
一旦遇到这种属性名称不确定的情况,就只能用一个变量代替,换句话说,不能用点号,只能用中括号。因此,当对象访问属性的时候,用中括号是更加灵活的。

JavaScript运算符

  1. JavaScript运算符,赋值运算、加、减、乘、除和取余数。
    var a;	//a=undefined
    var b = 10; 	//赋值运算符用于给JavaScript变量赋值
    var c = 2;
    var s1 = b+c;	//s1=12;
    var s2 = b-c;	//s2=8;
    var s3 = b*c;	//s3=20;
    var s4 = b/c;	//s4=5;
    var s5 = b%c;	//s5=1;取余数的意思是一个数字除以另一个数字,除不尽的部分就是余数。
    
  2. 还有自增运算符和自减运算符。自增运算符是++,自减运算符是 --。顾名思义,自增和自减运算符可以使得当前的变量自增一个单位或者自减一个单位。
    	var a = 1;
    	var b;	//b = undefined;
    	var s1 = (b = a++ + --a) + a-- + b++;
    // b = a++ + --a ;  => b = 2; a = 1;
    // s1 = (b = a++ + --a) + a-- + b++; => s1 = 5; a = 0; b = 3;
    

JavaScript数组

在JavaScript中,数组是一个非常灵活的类型。简单来说,数组就是一个容器,可以存放一个或者多个对象。当然,这些对象的类型是没有限制的,不管它是什么,数组都可以存放。

数组有4种定义方式

第一种是用直接量创建数组,剩下的3种都是用构造函数创建数组。其实用起来的话,还是第一种方式最好用,它是最简单的一种方式。

  1. 所谓直接量定义,就是用一对中括号声明一个数组对象。
    var arr = ["first","second","third"];
    console.log(arr);
    
  2. 采用构造函数的方式创建的一个数组对象,在JavaScript中,每个类型其实都有一个函数作为支撑,数组也不例外。在这个例子中,Array也叫作构造函数。
    	1. var a = new Array();		//直接用构造函数创建一个空的数组
    	2. var b = new Array(8);	//在创建的同时设置了一个初始的长度
    	3. var c = new Array("first","second","third");//在创建数组对象的同时就给它赋予了初值。
    	console.log(a.length);		// 0,数组天生就拥有一个length属性;
    	console.log(b.length);		// 8,没错,函数可以打括号,打括号的意思是执行这个函数的函数体。
    	console.log(c.length);		// 3,在创建数组的时候给它添加了3个元素。
    	console.log(b);		//结果是[<8 empty items> ]
    //数组只有一个属性,就是length。length表示数组所占内存空间的数目,而不仅仅是数组中元素的个数。
    

数组方法

  1. push方法,它可以把一个元素添加到数组里面。把数组想象成一个长长的盒子,我如果想要给数组添加新的元素,就可以用这个方法。
    	var b = new Array(8);
    	b.push("苹果");
    	b.push("香蕉");
    	b.push("牛油果");
    	console.log(b);
    //直接用push方法,那么元素就会被添加到数组的尾部,而且原来的8个位置无法占用,会直接跟在后面。
    

在这里插入图片描述

  • 数组本身有写数据的能力,只要给数组变量加上一对中括号,然后在中括号里面写上对应的下标位置,就可以给对应的内存空间塞入数据啦。
    var b = new Array(8);
    b.push("苹果");
    b.push("香蕉");
    b.push("牛油果");
    b[0] = "黄瓜";
    console.log(b);
    
  1. 删除数据需要用到数组的splice方法或者pop方法。先说pop方法,这个方法可以删除数组尾端的元素。
    var b = new Array(8);
    b.push("苹果");
    b.push("香蕉");
    b.push("牛油果");
    b[0] = "黄瓜";
    b[0] = "西瓜";
    b.pop();		//删除数组尾端的元素.
    console.log(b);
    
  • pop方法会默认删除数组中的最后一个元素。可以这么认为,先进入数组的后删除,后进入数组的先删除。
    在这里插入图片描述
  • 第二种,就是splice方法。splice方法的作用是插入、删除或者替换数组元素,它不仅会在原有的数组上进行修改,还会返回被处理的内容,因此这是一个功能强大但不容易使用的方法。splice方法用前两个参数进行定位,余下的参数表示插入部分
    	var a = [1,2,3,4,5];
    	a.splice(2,1);
    	console.log(a);	//运行结果为[1,2,4,5 ]。
    //splice方法的第一个参数代表需要操作的数组的起始位置,因为数组的下标位置默认从0开始。
    //splice方法的第二个参数代表要删除元素的个数。
    
  • 如果要把数字3替换成数字38,并且再在38的后面加一个元素66。
    	var a = [1,2,3,4,5];
    	a.splice(2,1,38,66);
    	console.log(a);		//运行结果为[1,2,38,66,4,5 ]。
    
  1. join方法可以把数组中的所有元素放入一个字符串。元素是通过指定的分隔符进行分隔的,而这指定的分隔符就是join方法的参数。
    var a = [1,2,3];
    var str = a.join(",");
    console.log(str);	//运行结果:1,2,3。
    

JavaScript函数

函数是一组可以被重复调用的代码语句。

函数七重关之一(函数定义)

  1. 第一种方法。
    	function test(){
    		alert("函数被调用了!");
    	}	
    /*
    函数的定义需要用到function关键字,函数名字是test,小括号里面是用来放参数的。
    也就是说,函数里面如果需要用到一些从外面传进来的数据,就可以通过参数变量做传递。
    最后就是函数体了,用大括号扩起来的部分就是函数的函数体。
    */
    	test();	//函数的调用方法就是在函数名称右边加一个小括号,表示要去执行函数的函数体了。
    
  2. 第二种方法。
    	var a = function(){
    		document.write("This is My function");
    		//document.write方法表示用JavaScript向页面输出一段话。
    	}
    /*
    第二种定义函数的方法需要先定义一个变量,比如‘var a’,
    然后还是用function关键字定义一个函数,再把这个函数赋值给变量a。
    因为最后要赋值给变量a,因此这里在定义函数的时候就不需要加上函数的名字了,这就是其中的一个区别。
    用这种方法定义出来的函数,函数的名字就是变量的名字,也就是说,我要想调用这个函数,就要这样做。*/
    	a();
    
  3. 区别
    第二个区别就体现在函数的调用上。
    	test();
    	function test(){
    		document.write("This is My function");
    	}
    // 代码运行后,成功在页面上打印出:This is My Function!
    
    	a();
    	var a = function(){
    		document.write("This is My function");
    	}
    // Uncaught TypeError: a is not a function
    
    /*
    如果是用第一种方法定义的函数,它会被提前加载,因此调用语句可以写在函数的定义之前,因为那个时候函数已经被加载完毕了。
    而用第二种方式定义的函数是不会被提前加载的,必须要执行到函数定义的语句才会加载这个函数,正因为这个道理,刚才的代码才会直接报错。
    因为在调用a函数的时候,a函数还没有加载,强行调用一个不存在的函数自然是不被允许的!*/
    
    举一个例子
    	console.log(a);
    	var a = function(){
    		alert("函数被调用了!");
    	}
    	console.log(a);
    //第一个a打印出来是undefined,表示未定义。
    //第二个a打印出来就是具体的函数了,说明这个时候函数a已经被加载完毕!
    
    注意:函数有没有被加载与变量有没有被定义是不同的事情。函数有没有被加载,可以看成function有没有被赋值给变量a。
    	console(apple);
       //  ReferenceError: apple is not defined
    
JavaScript编译原理
  1. JavaScript代码在运行之前会经过一个编译的过程,而编译有三个步骤。
    	var a = 10;
    	
    //第一个步骤是分词,得到的结果就是‘var、a、=、2、;’。
    //第二个步骤是解析,由JavaScript编译器对刚才分词得到的一个个代码块进行解析,生成一棵抽象的语法树(AST)。
    //抽象语法树定义了代码本身,通过操作这棵树可以精准地定位到赋值语句、声明语句和运算语句。
    //最后一个步骤,就是代码生成。最终生成出来的就是一些机器指令,创建了一个叫作a的变量并放在变量区,
    //然后分配一些内存以存放这个变量,最后将数字10存储在了变量a所在的地方。
    

通过JavaScript的解析器把它解析为一棵抽象树。

  • 首先是最顶层的大节点,也就是这棵树的顶端,上面清清楚楚地写着Program body,代表我们写的代码是一个程序。然后看这个程序里面的第一个也是唯一的一个子节点,上面清清楚楚地写着VariableDeclaration,意思就是变量声明。var a =10;这句话是一个程序,程序的目的是进行一个变量的声明。
    在这里插入图片描述
  • 在VariableDeclaration节点中包含两个子节点,一个是declarations[1],另一个是kind。declarations[1]是声明数组,中括号里面写了一个1,表示这个语句只声明了一个变量。kind代表种类,表示用var关键字声明一个变量。
    在这里插入图片描述
  • 展开里面的子节点,可以看到里面分为id和init两个子节点,id代表变量名,identifier是标识符,代表我们的变量名,也就是a。init表示变量的初始化操作,从语句上也能看出,它是将10赋给变量a。
    在这里插入图片描述
  • 如果没有给变量a赋值,那么JavaScript的解释器也会给变量a赋一个初始值,null代表空。注意:这里的null不要理解为JavaScript里面的数据类型null,而是语义上的空。实际上,在代码执行的时候,变量a的值是undefined。
    在这里插入图片描述
  • 代码中多了一个console.log输出语句,在生成的抽象语法树上,又结出了一个新的果实——ExpressionStatement(表达式语句)。表达式语句就是普遍意义上的一行JavaScript代码。console是一个内置对象,log是console对象的一个方法,变量a作为参数传入了log方法。总体来说,这就是一个函数的调用语句。
    var a;
    console.log(a);
    
    在这里插入图片描述
    小提示:抽象语法树的创建可以在网站https://esprima.org/demo/parse.html上自行调试和验证。

函数七重关之二(作用域)

  1. 在JavaScript中,作用域分为两种,一种是全局作用域,另一种是函数作用域。所谓作用域,就是指当你要查找某一个变量的时候,你可以在什么范围内找到这个变量。这个寻找的范围,就是作用域。不管是全局作用域还是函数作用域,都被定义在词法阶段。词法阶段就是刚才所说的JavaScript编译代码的第一个步骤——分词。所以,词法阶段也叫作分词阶段。
    	function test(){
    		console.log(a);
    	}
    	var a = 10;
    	test(); //10;
    /*变量a和test函数都直接暴露在外面,都属于全局作用域。
    而test函数的函数体,即用花括号包起来的部分(函数的函数体)则是函数作用域。
    又因为test函数属于全局作用域,而它自己又拥有一个函数作用域,那么这样一来,就形成了一个作用域的嵌套。*/
    
  2. 当发生作用域嵌套的时候,只能里面的访问外面的,外面的无法访问里面的。而且需要注意一点,那就是作用域嵌套一般是针对全局作用域和函数作用域,或者是函数作用域和其他函数作用域而言的。
    	if(false){
    		var a = 20;
    	}
    	console.log(a);	//a = undefined;
    /*var a = 20;这句话在if判断中,而if判断的条件是false,所以这句话的确不会执行。
    但是,执行代码是在运行阶段,在代码的分词阶段和解析阶段,变量a依然会被获取,并且系统会默认给它一个undefined。
    又因为变量a不是在某一个函数的函数体中,而是在全局作用域里面,
    所以console.log方法依然可以访问这个变量,因此获取变量a的值就是undefined。*/
    
  3. 举例
    	var a = 10;
    	function test(){
    		var a;
    		function inner(){
    			console.log(a);
    		}
    		inner();
    	}
    	test();	//undefined;
    /*在函数作用域里面嵌套了函数作用域,那么在最里面的inner函数中访问一个变量,就会优先在inner函数里面寻找,在当前函数作用域里面找不到,
    那么就往上翻一层,在它的父级作用域,也就是test函数的作用域里面寻找,结果发现找到了。test函数里面定义了一个变量a,但是没有赋值,
    那么a就是undefined。既然已经找到了,那么就不会去全局作用域里面寻找变量a了。所以,全局作用域里面的变量a其实就是一个摆设。*/
    

函数七重关之三(参数传递)

所谓的参数,就是指当函数调用的时候会传进来的值,也就是说,我们在定义参数的时候并不知道调用的过程中会有什么样的值传过来。

  1. 参数传递
    	function add(a,b,c){
    		var sum = a + b + c;
    		console.log(sum);
    	}
    	add(1,2,3); //6
    //sum是定义在add函数的函数体内部的,外面的全局作用域是没有办法直接访问函数作用域里面的sum变量的。
    //只能在该函数的函数体内被访问,它也被叫作局部变量。
    
  2. 调用函数的时候少传了参数。
    		function add(a,b,c){
    			var sum = a + b + c;
    			console.log(sum);
    		}
    		add(1); //结果是NaN,代表无法计算。a的值是1,b和c的值就是undefined,
    	//任何变量在被赋予真正的值之前,其在编译阶段都是undefined。
    	//函数的参数可以被理解为一种预备变量,传参的过程就相当于是给预备变量赋值的过程。
    
  3. 调用函数的时候多传了参数,在函数里面访问额外的参数。
    	function add(a,b,c){
    		console.log(arguments);
    		var sum = a + b + c;
    		console.log(sum);
    	}
    	add(1,2,3,4); // 结果是6,强行加了第四个参数,对结果也不会有什么影响。
    //其实所有的参数都会被装载到函数内部一个叫作arguments的数组里面。
    
    代码运行结果 在这里插入图片描述
  4. 参数个数任意,实现数字的累加。
    		function add(){
    			if(!arguments[0]){
    				return 0;
    			}
    			for(var i = 1; i < arguments.length; i++){
    				arguments[0]=arguments[0]+arguments[i];
    			}
    			console.log(arguments[0]);
    		}
    		add(1,2,3,4);
    	//因为在实现之前并不知道会有几个参数传进来,所以干脆就不设置任何参数了。
    	//先用arguments数组获取第一个位置的元素。如果第一个位置的元素不存在,那么就返回0。
    	//在函数的函数体中可以通过arguments数组的length属性预知未来传入参数的个数。
    

函数七重关之四(闭包)

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure),也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。

在 JavaScript中,每当创建一个函数,闭包就会在函数创建的同时被创建出来,作为函数内部与外部连接起来的一座桥梁。

  1. 函数返回值。

    	function add(){
    		var sum = 0;
    		for(var i = 0; i < arguments.length; i++){
    			sum = sum + arguments[i];
    		}
    		return sum;
    	}
    	var sum = add(1,2,3,4);
    /*sum是函数作用域里面的变量,因此也叫局部变量,外面是没有办法直接访问这个局部变量的,
    除非把sum作为返回值返回出去,外面才可以访问sum变量。*/
    
  2. 典型的闭包

    	function test(){
    		var a = 0;
    		return function(){	//匿名函数
    			console.log(a);
    		}
    	}
    //因为test函数返回的结果是一个函数,不去调用的话就不会执行里面的代码,需要执行内部函数的函数体。
    	test()();
    //第一个小括号是调用test函数,这个test函数中定义了一个局部变量a,还返回了一个内部函数。
    //第一次调用的结果就是返回一个内部函数,而第二个圆括号才会调用那个内部函数。”
    

    总结一下产生闭包的条件

    第一点,在函数内部也有一个函数。就好比这个例子,在test函数里面还有一个函数。
    第二点,函数内部的函数里面用到了外部函数的局部变量。还是这个例子,test函数里面有一个局部变量a,并且被内部函数使用了。
    第三点,外部函数把内部函数作为返回值return出去了。

  3. 使用了闭包,那么就会让这个局部变量不随着原函数的销毁而销毁,而是继续存在。利用闭包操作可以减少很多不必要的全局变量。

    	function test(){
    		var a = 0;
    		return function(increment){	
    			a += increment;
    			console.log(a); 
    		}
    	}
    	var inner = test();	//先获取这个内部函数。
    	inner(1);	//第一次调用内部函数
    	inner(1);	//第二次调用内部函数
    	inner(1);	//第三次调用内部函数
    	//代码执行结果分别为1、2、3。
    
    /*正常情况下,我们调用一个函数,其里面的局部变量会在函数调用结束后销毁,这也是我们在全局作用域里面无法访问函数局部变量的原因。
    给内部函数设置一个累加的参数,在每次调用内部函数的时候都把这个参数的值加上。证明打印出来的是同一个变量。*/
    

函数七重关之五(自执行函数)

只想执行一个函数,却无所谓这个函数叫什么名字。那么在这种情况下,就可以考虑使用自执行函数了。

  1. 自执行函数的格式 。

    语法: (定义一个没有名字的函数)()

    (
    	function(){
    		console.log(123);
    	}
    )();
    //所谓自执行函数,顾名思义,就是在定义之后就立刻执行的函数,它一般是没有名字的。
    //也正因为自执行函数没有名字,所以它虽然会被立刻执行,但是它只会被执行一次。
    
  2. 自执行函数一般可以和闭包配合使用

    var inner = (function() {
    	var a = 0;
    	return function(increment){	
    		a += increment;
    		console.log(a); 
    	}
    }
    )();
    inner(1);	//第一次调用内部函数
    inner(1);	//第二次调用内部函数
    inner(1);	//第三次调用内部函数
    //直接得到闭包环境下的内部函数了,外部函数只是为了产生闭包环境而临时定义的函数,正因为如此,所以根本没有必要给外部函数取一个名字!
    

函数七重关之六(“new”一个函数)

  1. this永远指向当前函数的调用者。
    	function hello(){
    		console.log(this);
    	}
    	window.hello();
    //首先,这句话透露出的第一个信息是,this要么不出现,一旦出现,就一定出现在函数中。
    //第二个信息是,this指向函数的调用者,换句话说,这个函数是谁调用的,那么this就是谁。
    //我们调用hello函数,其实也就是window对象调用了这个hello函数。hello函数里面的this自然就指向了window对象。
    
  2. 在调用函数的时候使用了new。
    	function hello(){
    		console.log(this);
    	}
    	new hello();
    //hello函数内部产生了一个新的对象,也就是hello函数的真实调用者——this关键字指向的那个对象。函数默认返回了这个新的对象。
    
    	var newObject = new hello();
    	console.log(newObject);
    //newObject就是函数里面的this,也就是函数内部新产生的那个对象了。
    //这种函数叫作构造函数。通过这种方式,我可以通过构造函数构建一个对象模板。
    
    在这里插入图片描述
  3. 所谓对象模板,就是指用一个函数的形式设计一种对象的种类。
    function Fruit(name,smell,color){	//一般来说,如果这是一个构造函数,那么首字母就需要大写。
    	this.name = name;
    	this.smell = smell;
    	this.color = color;
    }
    var apple = new Fruit("苹果","香甜可口","红色");
    
    在这里插入图片描述
  • 自定义对象
    var apple2 = {
    	name : "苹果";
    	smell : "香甜可口";
    	color :	"红色"
    }
    
  • 用构造函数定义对象是有优势的。比如我需要2个苹果,使用构造函数的话,直接调用两次new函数就行了,可以非常方便地获得两个苹果。而使用大括号的方式就得写两次。
    var apple1 = new Fruit("苹果","香甜可口","红色");
    var apple2 = new Fruit("苹果","香甜可口","红色");
    
    var apple3 = {
    	name : "苹果";
    	smell : "香甜可口";
    	color :	"红色"
    }
    var apple4 = {
    	name : "苹果";
    	smell : "香甜可口";
    	color :	"红色"
    }
    
    
    var apple5 = apple3; //apple3和apple5其实都是指向同一个苹果的。
    var a1 =10;
    var a2 = a1;
    /*a1和a2还是不同的数据,虽然都是10,但是在内存上却处于不同的空间。
    而引用数据类型则不同,如果简单地分出一个变量区和内存区,apple3和apple5就都属于变量区的两个不同的变量了,但是却指向同一块内存地址,也就是真实的对象地址。
    这样一来,不管是apple3还是apple5,它们都拥有操作这一块内存区域的权限,也就是说,它们都可以修改真实对象的属性值。*/
    

函数七重关之七(回调函数)

所谓回调函数,就是指把一个函数的定义当作参数传递给另一个函数。

  1. 回调函数
     function addSqua(num1, num2, callback){
        var sum = num1 + num2;
        return callback(sum);
      }
      
      function squa(num){
        return num*num;
      }
      
      let num = addSqua(1, 2, squa);    
      console.log(num);		//=>9  
    
  2. 匿名回调函数
    function addSqua(num1, num2, callback){
     var sum = num1 + num2;
     return callback(sum);
    }
    let num = addSqua(1, 2, function squa(num){
     return num*num;
    });    
    console.log(num); 	//=9 
    

精度问题,化浮为整

  1. 计算0.1+0.2。
<!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>Document</title>
</head>
<body>
    <script>
        function add(num1, num2) {
            // 将数字换成字符串;
            num1 = num1.toString();
            num2 = num2.toString();
            // 获取小数点位置;
            var index1 = num1.indexOf(".");
            var index2 = num2.indexOf(".");

            //如果小数点存在,则获取各自的小数位数。
            var ws1 = 0;
            var ws2 = 0;
            if (index1 != -1) {
                ws1 = num1.split(".")[1].length;
            }
            if (index2 != -1) {
                ws2 = num2.split(".")[1].length;
            }

            //获取小数位数大小;
            var bigger = (ws1 > ws2) ? ws1 : ws2;
            var smaller = (ws1 < ws2) ? ws1 : ws2;

            //计算得到需要补齐的0的个数
            var zerosCount = bigger - smaller;

            //去除小数点;
            num1 = num1.replace(".", "");
            num2 = num2.replace(".", "");

            //比较num1 和 num2;看谁是smaller,是smaller的一方需要补0;
            if (ws1 == smaller) {
                for (var i = 0; i < zerosCount; i++) {
                    num1 += "0";
                }
            } else {
                for (var i = 0; i < zerosCount; i++) {
                    num2 += "0";
                }
            }

            //开始计算
            var num = parseInt(num1) + parseInt(num2);

            //根据较大的小数位数计算倍数
            var beishu = 1;
            for (var i = 0; i < bigger; i++) {
                beishu = beishu * 10;
            }
            num = num / beishu;
            return num;
        }
        console.log(add(0.1, 0.2));
    </script>
</body>
</html>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript基础 的相关文章

  • Jasmine 条件 callThrough 和 callFake

    我有一个返回函数引用的方法 function methodetobeMoked param case1 return func1 case 2 return func2 case n return funcN 我需要监视这个方法并返回特定输
  • Exceljs:迭代每行和每列的每个单元格

    我想在所有单元格中添加粗边框 这是一个有角度的项目 我正在使用打字稿 我可以为 1 个单元格做到这一点 worksheet getCell A1 border top style thick left style thick bottom
  • 如何在React-Native中选择ListView的一项?

    我是 React Native 的新手 我想使用 ListView 选择一项 当我第一次按下 item 时 ListView renderRow 被调用 但终究不起作用 我该如何修复这个错误 我的问题出在哪里 我写了一个演示here htt
  • 如何使用 Underscore 获取 JavaScript 数组中的重复项

    我有一个数组 我需要重复的项目并根据特定属性打印这些项目 我知道如何使用 underscore js 获取唯一项目 但我需要找到重复项而不是唯一值 var somevalue name john country spain name jan
  • 使用 Charts.js 禁用动画

    我在使用 Charts js 关闭动画时遇到一些问题 这是我的代码 var pieData value 30 color F38630 value 50 color E0E4CC value 100 color 69D2E7 var myP
  • 为什么我的箭头函数有原型属性?

    正如文档中提到的https developer mozilla org en docs Web JavaScript Reference Functions Arrow functions https developer mozilla o
  • 粉碎一个元素,向随机方向发送碎片

    我试图 粉碎 一个元素 例如 一个图像 并将其碎片朝随机方向飞行 当碎片到达目的地时 即x距离 以像素为单位 它们变成原始图像的较小版本 jQuery UI 的explode http api jqueryui com explode ef
  • 如何在Javascript中声明静态变量[重复]

    这个问题在这里已经有答案了 在下面的代码中 我希望有一个计数器来跟踪创建的 Person 对象的数量 这段代码没有这样做 我该如何实现呢 function Person this name Peter this counter this c
  • 如何获得相对于特定父级的偏移量?

    我想获取元素相对于的偏移量特定的父母不是直接的 也不是文档 我在互联网上查找并找到了offset http api jquery com offset and position http api jquery com position jQ
  • 按日期对 JSON 进行排序

    我知道这一定相对简单 但我有一个 JSON 数据集 我想按日期排序 到目前为止 我每次都会遇到问题 现在我将日期存储为this lastUpdated 如果有帮助的话 我可以访问 jquery 但我意识到 sort 是本机 JS 提前致谢
  • 从对象获取数据 - 我看到数据但无法保存它们

    正如你所看到的 我是新来的 我确实尝试过搜索 但没有找到解决我问题的方法 所以这是我的问题 如果我这样做 console log grid data kendoGrid data 这在控制台中显示如下 所以我明白这一点 有一个数组和一个带有
  • toJSON() 和 JSON.Stringify() 之间的区别

    如果您需要读取或克隆模型的所有数据属性 请使用其 toJSON 方法 此方法返回属性的副本作为 对象 尽管有其名称 但不是 JSON 字符串 当 JSON stringify 为 使用 toJSON 方法传递一个对象 它将返回的字符串化 t
  • jQuery 如何通过不同的列值计算表中的行数

    如何按表列计算不同的表行 Example table thead tr th NAME th th TECHNOLOGY th tr thead tbody tr td john td td jQuery td tr tr td mark
  • 用于图形操作的 Javascript 库

    有没有建议的 javascript 替代 pythonpygraph http code google com p python graph or NetworkX http networkx lanl gov 应该注意的是 可视化不是必需
  • JavaScript 按名称获取当前作用域中的变量

    所以我有一个变量和该变量名称的字符串 function Factory string var foo bar console log foo is equal to this string 如果变量所在的对象是当前对象 如何从字符串文字中获
  • 按钮导致页面重新加载

    我在我的页面上使用 html 和 jquery 在我的 html 中 我有一个按钮 单击该按钮将触发一个功能 当页面加载时 我调用文档准备中的主函数 这是我的代码 div div
  • 限制线的长度

    我正在尝试画一条代表 弹弓 的线 并且希望它具有最大拉伸长度 在 p5 中 我在位置和位置之间画了一条线 line posA x posA y posB x posB y posA 是鼠标 x 和 y posB 是画布上圆的位置 我想要做的
  • onClick 事件适用于触摸屏设备上的触摸吗?

    我用过onclick我的网站上的活动 但是 当我在谷歌浏览器的开发人员模式移动视图中打开它时 触摸使用鼠标单击的元素没有任何反应 所以我的问题是 我还必须添加吗ontouch事件连同onclick事件或 onClick 事件适用于所有触摸屏
  • 使用 Three.js 中的设备方向控件进行对象旋转

    我正在迈出使用 JavaScript 进行编码并使用 Three js 的第一步 我正在尝试 Threejs org 的这个例子 http trijs org examples misc controls deviceorientation
  • 调试客户端时使用 Chrome/Firefox

    我正在使用带有 getUserMedia 的相机 但出现了一些需要修复的错误 问题是 Visual Studio 只允许我使用 IE 调试 JavaScript 我的意思是命中断点 而 IE 不支持 getUserMedia 如果您想在 I

随机推荐

  • LeetCode:三数之和&四数之和

    1 方法概述 1 前期处理 三数之和用三个指针 四数之和用四个指针 最开始都要进行从小到大的排序 2 粗处理 编写三数之和的时候第一个指针刚开始指向所给数组的第一个元素 第二个指针记为L指针 初始指向第一个指针所指元素的下一个元素 第三个指
  • 福兔迎春,春节快乐

  • ajax异步问题导致的刷新页面数据不更新

    ajax的async默认的设置值为true 这种情况为异步方式 就是说当ajax发送请求后 在等待server端返回的这个过程中 前台会继续 执行ajax块后面的脚本 直到server端返回正确的结果才会去执行success 也就是说这时候
  • Unity飞船摄像机360度环绕(逐步完善)

    极简版 目标飞船 public Transform target 摄像机距离 public float distance 100 void Update float mouseX Input GetAxis Mouse X float mo
  • Nginx知识总结

    1 简介 Nginx engine x 是一个高性能的HTTP和反向代理web服务器 同时也提供了 IMAP POP3 SMTP服务 Nginx是由伊戈尔 赛索耶夫为俄罗斯访问量第二的 Rambler ru站点开发的 第一个公开版本0 1
  • matlab制作旋转动态图,matlab 如何画动态图(绘图与旋转视图)

    效果图 在matlab中 作图是重要的一部分 那么对于三维的图像 如何将静态的改为动态的呢 首先 静态图的代码 t 0 0 1 20 i 1 200 这里只是画了一个点 而 绘图 效果图 在matlab中 作图是重要的一部分 那么对于三维的
  • docker compose 部署skywalking

    文章目录 前言 架构图 docker compose 脚本 整合springboot 前言 SkyWalking 是一个开源的 APM 系统 核心功能如下 服务 服务实例 端点指标分析 根本原因分析 服务拓扑图分析 服务 服务实例和端点依赖
  • 【SQL注入-12】http头部注入案例—基于Sqli-labs靶机(借助BurpSuite工具)

    目录 1 概述 1 1 User Agent概述 1 2 Referer 概述 2 实验平台及实验目标 2 1 实验平台 2 2 实验目标 3 User Agent注入案例 以sqli labs Less18为例 3 1 注入前准备 3 2
  • 【翻译】 DMA和get_user_pages()

    LWN net需要你 没有订阅者 LWN将根本不存在 请考虑注册订阅 帮助LWN继续出版 作者 Jake Edge 2018年12月12日 Linux管道工会议 在2018年Linux Plumbers大会 LPC 的RDMA微型会议上 J
  • 数据理解与数据准备

    1 数据类型 属性类型 属性的取值范围决定了属性的类型 定性数据 标称属性 多分类变量 二元属性 01变量 序数属性 有序分类变量 定量数据 区间标度属性 比率标度属性 区分这两种属性的原则是该属性是否有固定的零点 根据表现出来的数值特点
  • Spring关于数组、集合和Properties的注入

    在某个类中需要依赖其它类时 通常是new一个依赖类再调用类实例的方法 这种开发存在的问题是new的类实例不好统一管理 Spring提出了依赖注入的思想 即依赖类不由程序员实例化 而是通过Spring容器帮我们new指定实例并且将实例注入到需
  • prometheus实战指南

    一 prometheus概述 1 Prometheus简介 Prometheus是一套开源的系统监控报警框架 作为新一代的云原生监控系统 目前已经有上千个贡献者参与到Prometheus的研发工作上 并且超过120 项的第三方集成 Prom
  • 知识图谱快速入门

    图技术 利用neo4j networkx dgl python做图分析挖掘 1 最短路径算法dijkstra 2 基于networkx的隐性集团关系识别模型 3 基于Neo4j的担保社群型态分析挖掘 4 基于python求有向无环图中tar
  • Matrix 【POJ - 2155】【二维线段树+永久化标记】

    题目链接 挺好的一道题 一开始用lazy标记往下推 总是推不出样例的正解 然后就去看了相关博客 发现却确实如此 在这里是无法用lazy标记来层层推的 并且还会出现超内存的情况 所以 便改用了永久化标记来解这道题 还有一件是 关于discus
  • IMU姿态计算

    总述 IMU即惯性测量单元 主要用于对机体的加速度与角速度的测算 使用场景很多 例如 平衡车 惯性导航等等 姿态 姿态角 Euler angles 是用于描述物体在三维空间中的旋转姿态的一种表示方法 它由三个角度组成 通常表示物体绕三个轴
  • html3D创意相册附源码

    3D创意相册 效果图如下 文件目录 图片名称 jpg index html
  • VB+SQLite组合,真香!(一)

    微信公众号 网管小贾 个人博客 www sysadm cc 嗨 你好 我是网管小贾 当你看到这个标题时 是不是感觉很奇怪呢 老掉牙的VB和 玩具 数据库SQLite搞在一起是个什么玩意 待我慢慢道来哈 SQLite众所周知 是个文件数据库
  • 非常厉害的全文检索技术Elasticsearch

    目录 一 Elasticsearch是什么 二 关于安装 三 kibana的安装和使用 最后 今天是刘小爱自学Java的第157天 感谢你的观看 谢谢你 全文检索技术Elasticsearch的学习 牵扯到的知识点太多太多了 首先要创建一个
  • Cadence Allegro PCB设计88问解析(九) 之 Allegro中封装(footprint)3D模型添加

    一个学习信号完整性的layout工程师 今天整理下PCB封装的3D 模型添加 此步骤并不是所有的公司使用 因为我们平常给器件添加一个实际的高度 就已经OK了 只不过我们在看整版的3D模型是 每个器件都是方方正正的 不太美观 所以有的人要求完
  • JavaScript基础

    编程之修 重在积累 而非资质 资质虽然重要 可是后天的努力更不可缺少 直接量 编程世界中的直接量 就是表面上可以见到的数据值 常见的直接量有数字 小数 字符串 字符串的出现必然带着双引号 单引号也可以 被很好地包裹住 而数字则是光秃秃的 如