js核心语法总结
1.变量
什么是变量?变量是存储信息(数据)的容器。
定义变量时不需要申明具体类型,其类型由赋值的变量决定;
变量的三要素:
1,数据类型:决定在内存中分配的空间
2,变量名:空间别名
3,值:空间中存储的数据
变量的三种定义方式?const、var、let三种定义变量方式的区别
1.const一般用来定义常量,一旦被定义就不可以被修改,而且必须初始化
2.var定义的变量可以修改,但是如果声明了不赋值就会输出undefined,不会报错
3.let是块级作用域,函数内部使用let定义后,对函数外部无影响
2.数据类型
js中有七种数据类型:
1.五种简单数据类型(基本数据类型) Number ,String,Boolean,Null,Undefined
简单类型(基本类型)的值在内存中占据固定大小的空间,被保存在栈内存中。(从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本);
2.一种 复杂数据类型(引用数据类型) Object (Array,Math,Data,Function)
3.symbol(es6)
小结
基本数据类型(值类型):number,string,boolean
复杂类型(引用类型):object
空类型:undefined,null
值类型的值存储在哪里? 栈中存储具体的值
引用类型的值存储在哪里?栈存放地址和堆中存放代码块
值类型赋值传递的是值。
`引用类型赋值,传递的是地址。`
2.1简单数据类型 Number 数值型
包含整数和浮点数
2.1.1.浮点数会自动转换为整数
let num =1.00;
console.log(num)//输出1,自动转换为了整数
2.1.2浮点数的最高精度是17位
let a = 0.1;
let b = 0.2;
console.log(a+b);//输出0.30000000000000004
2.1.3.NaN:非数字类型。
特点:① 涉及到的 任何关于NaN的操作,都会返回NaN
② NaN不等于自身。
2.1.4. isNaN() 函数用于判断是否是一个非数字类型,如果传入的参数是一个非数字类型,那么返回true,否则返回false
2.1.5.isNaN()函数传入一个参数,函数会先将参数转换为数值,如果参数类型为对象类型,会先调用对象的valueOf()方法,再确定该方法返回的值是否可以转换为数值类型,如果不能,再调用对象的toString()方法,再确定返回值
(valueOf() 返回原始值)
数值转换
Number( )转换为函数,可以用于任何数据类型
parseInt( ),将值转换为整数型
parseFloat( ),将值转换为浮点数型
2.2简单数据类型 String 字符串型
1.字符串类型中的单双引号的作用效果完全一样
2.字符串类型有length属性,可以取得字符串的长度
let str = 'hello world'
console.log(str.length)//输出 11,包含了空格
3.字符串的值是不可变的,要改变一个字符串的值,首先要销毁原来的字符串,再用另一个包含新值得字符串区填充该字符串;
字符串转换
String( ),转型函数,适用于任何数据类型(null、undefined还是自身)
toString( ),方法可以把一个逻辑值转换为字符串,并返回结果(null、undefined没有这个方法)
number.toLocaleString( ) ,把数字转换成本地格式的字符串
字符串的运算
字符串与数字相加,结果是字符串的拼接,在js里,字符串加一个加号+代表拼接的意思,-*/等运算存在隐式转换,将字符串转换为数值进行运算了
let str = '10';
let n = 5;
console.log(n + str);//510
console.log(n - str);//-5
2.3简单数据类型 Boolean 布尔值
1.只有两个值,true和false,用来判断
2.Boolean( ),将某个值转换为Boolean型
**.2.4简单数据类型 Null 空对象指针 **
如果定义了一个变量,但是想在以后把这个变量当做一个对象来用,那么最好将该对象初始化未null值
2.5简单数据类型 Undefined 未定义
1.只有一个值,即undefined,使用var声明了一个变量,但未给变量初始化值,那么这个变量的值就是undefined
2.调用函数时,应该提供的参数没有提供(没有传参),该参数就等于undefined
3.对象没有赋值的属性,该属性的值为undefined
4.函数没有返回值,默认返回undefined
2.6 Object类型 引用数据类型(复杂数据类型)
JS中的三种对象:
-
内置对象
Math,Date,String,Array,Object
内置对象中Math的常用方法 Math.PI Math.abs() Math.ceil()//取大值 Math.floor()//取小值 Math.max()//最大值 Math.min()//最小值 Math.pow() Math.sqrt() Math.random()//生成一个大于零小于一的随机数
-
自定义对象
-
浏览器对象
2.6.1 引用类型的值可以改变
let person = {name: 'lisa'}
person.name = 'Jane' // 通过修改对象属性值更改对象
console.log(person) // 输出:{name: "Jane"}
person.age = 18 // 给对象增加一个属性
console.log(person) // 输出:{name: "Jane", age: 18}
let arr = [1, 2, 3, 4, 5]
arr[0] = 0 // 更改数组的某一个元素
console.log(arr) // 输出:[0, 2, 3, 4, 5]
arr[5] = 6 // 给数组增加一个元素
console.log(arr) // 输出:[0, 2, 3, 4, 5, 6]
2.6.2 引用类型可以添加属性和方法
let person = {}
person.name = 'lisa'
person.say = function () {
alert('hello world')
}
console.log(person.name) // 输出:lisa
console.log(person.say) // 输出:function () { alert('hello world') }
2.6.3引用类型的赋值是对象引用
let a = {}
let b = a
a.name = 'lisa'
console.log(a.name) // 输出:lisa
console.log(b.name) // 输出:lisa
b.age = 18
console.log(a.age) // 输出:18
console.log(b.age) // 输出:18
2.6.4当从一个变量向另一个变量赋值引用类型的值的时候,同样会将存储在变量中的对象的值复制一份到为新变量分配的空间中,引用类型保存在变量中的是在堆内存中的地址。所以,与基本数据类型的简单赋值不同,这个值的副本实际上是一个指针,而这个指针指向存储在堆内存中的一个对象,那么赋值操作后,两个变量都保存了同一个对象的地址,而这两个地址都指向了同一个对象,因此,改变其中任何一个变量,都会互相影响。
所以,引用类型的赋值其实是对象保存在栈区地址指针的赋值,所以两个变量指向同一个对象,任何的操作都会互相影响。
2.6.5引用类型的比较是引用的比较
let man = {}
let woman = {}
console.log(man === woman) // 输出:false
看上面的例子发现,两个对象一模一样,但是却不相等。因为引用类型的比较是引用的比较,换句话说,就是比较两个对象保存在栈区的指向堆内存的地址是否相等,此时,虽然man和woman看起来都是一个“{}”,但是他们保存在栈区中的指向堆内存的地址却是不同的,所以两个对象不相等。
2.6.6 引用类型是同时保存在栈区中和堆区中的,引用类型的存储需要在内存的栈区和堆区中共同完成,栈区保存变量标识符和指向堆内存的地址:
let a = {name: 'aaa'}
let b = {name: 'bbb'}
let c = {name: 'ccc'}
上面三个对象的内存中保存情况如下图:
object.constructor 对象的constructor属性引用了该对象的构造函数
属性常用于判断未知对象的类型。给定了一个未知的值,就可以使用typeof运算符来判断它是原始的值还是对象。如果它是对象,就可以使用constructor属性来判断对象的类型。
function isArray(x) {
return ((typeof x == "object") && (x.constructor == Array));
}
但是要注意,虽然这种方法适用于JavaScript核心语言的内部对象,但对于“主对象”,如Window这样的客户端JavaScript对象,这种方法就不一定适用了。Object.toString()方法的默认实现提供了另一种判断未知对象类型的方法。
用自定义构造函数创建对象时,发生了什么?
- 申请内存空间,存储创建的新的对象
- 把this设置成当前对象
- 设置对象的属性和方法值。
- 把this这个对象返回。
创建对象时保存在堆上的,实例是取存放在栈中的当前对象的地址的引用。
字面量创建的对象属于一次性的对象。
访问对象的属性另外一种形式,通过[属性名],索引到某个属性。
JSON对象
Json对象不论是键,还是值都是用双引号引起来的。
let json = {
'name':'jack',
'sex':'male',
'age':'6'
}
Json数据的遍历,不能使用for循环,但是可以使用for-in
let json = {
'name':'jack',
'sex':'male',
'age':'6'
}
for (key in json){
console.log(key,json[key])
}
//name jack
//sex male
//age 6
Array数组
1.什么是Array数组?数组的概念?
Array数组对象 是用于在单个的变量中存储多个值,数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式。
2.创建Array对象的语法:
1.字面量
var arr = []; // 使用数组字面量方式创建空的数组
var arr1= ['小白','小黑','大黄','瑞奇']; //使用数组字面量方式创建带初始值的数组
数组的字面量是方括号[]
声明数组并赋值称为数组的初始化
这种字面量的方式也是我们以后用的最多的方式
所有的数组都继承于 Array.prototype。
2.构造函数 利用new 创建数组
var arr3 = new Array(); //创建了一个新的空数组
注意Array(),A要大写
3.数组元素的类型
数组中可以存放任意类型的数据
var arr4 = ['小白',12,true,12.3];
4.获取数组中的元素
index索引(下标):用来访问数组元素的序号(数组下标从0开始),
arr[index]:访问、设置、修改对应的数组元素,
5.遍历数组
把数组中的每个元素从头到尾都访问一次,可以通过for循环索引遍历数组中的每一项
var arr = ['red','green','blue'];
for(var i = 0;i < arr.length; i++){
console.log(arr[i])//打印数组的每一个元素
}
ForEach()中的回调函数第一个参数为元素,第二个参数为索引
let arr = [10,'10',true,null,undefined,new,Object()];
console.log(arr);
arr.forEach(item,index)=>{
console.log(item,index)
}
//打印结果
10 0
10 1
true 2
null 3
undefined 4
{} 5
数组去重
/* 数组去重 */
let arr = [0,0,0,1,1,1,2,3,3,3,'a','b','a'];
Array.prototype.unique = function(){
let tmp = {};
let newArr = [];
for(let i=0;i<this.length;i++){
if(!tmp.hasOwnProperty(this[i])){
tmp[this[i]] = this[i];
newArr.push(this[i]);
}
}
return newArr;
}
console.log(arr.unique()); // [ 0, 1, 2, 3, 'a', 'b' ]
Function函数
1.什么是函数?函数的概念
函数是一个对象,可以被其他代码或者其自身调用的代码片段,或者是一个指向该函数的变量。当函数被调用时,参数被作为输入传递给函数,并且函数可以返回输出。
函数声明之后需要调用才会执行,不调用不执行。函数名是作为函数声明或函数表达式的一部分声明的标识符。函数的作用域取决于函数名是一个声明还是表达式。
函数的定义方式:
函数表达式:把一个匿名函数赋值给一个变量
let fn = function(){
console.log('函数表达式')
};//函数表达式后面需要加上分号
console.log(fn)
如果函数声明被覆盖,无论在什么位置调用函数,都是调用新声明的函数。
f1();//English
function f1(){
console.log('中文')
};
f1();//English
function f1(){
console.log('English')
};
f1();//English
若函数表达式被覆盖,则调用函数执行当前声明的函数
var f1 = function(){
console.log('中文')
};
f1();//中文
f1 = function(){
console.log('English')
};
f1();//English
2.函数的类型
2.1 匿名函数 :是一个没有函数名的函数
function (){};//匿名函数
()=>{}; // 匿名箭头函数
2.2命名函数 :是具有函数名称的函数
function fn(){}; //名称为fn的命名函数
const fn = () => {};// 名称为fn的箭头函数
2.3内部函数 :是一个函数内部的函数(下面例子中的fn2);
外部函数 :是包含一个函数的函数(下面例子中的fn1);
function fn1(a,b){ //外部函数
function fn2(e){ //内部函数
return e*e;
}
return fn2(a) + fn2(b);
};
//箭头函数
const fn1 = (a,b) => { //外部函数
const fn2 = e => e*e; //内部函数
return fn2(a) + fn2(b);
};
2.4递归函数 :
定义:
递归函数就是在函数体内运行过程中调用自己;本质上就是循环.
构成递归的条件有:
1.不能无限制的调用本身,必须有一个出口,化为简单的状况处理(非递归状况)
2.子问题和原始问题为同样的事情,且子问题更为简单.
由于递归是函数本身一层层压栈,导致先入栈的不能出栈,空间占满以后就会造成堆栈溢出的现象.所以需要退出条件return,否则会陷入死循环。
递归的处理机制
1.举例引入
举个栗子,电脑桌面有一个文件夹,文件夹里面有我需要的文件(如 xx.txt),但是又存在着许多的子文件夹,子文件夹里面又有子文件夹,而我需要的实体文件又散落在这些文件夹里面。此时,我需要精准有效地将所有的实体文件找出来,并备份,该怎么办?
假设:下方的红圈代表整个文件夹,蓝圈代表子文件夹,各个数字为实体文件。
实现:精准地获取文件也就是意味着,需要访问每一个子文件;有效地获取,也代表着不能胡乱寻找,得依次序,确保不遗漏。当我们按图中的方式开始依次找寻,每找到一个,就进行文件备份,到最后,就能解决这个问题。
转换思维:当我们用程序的思维来理解找文件的过程,又是什么样的呢?
我们要找一个文件,是不是就相当于执行一个函数,我们提供参数,也就是文件夹,然后执行找文件的动作,得到返回值,也就是实体文件或其他。
// 找文件的方法
function 找文件(文件夹){
// 找文件的动作
return 文件 || 空 ;
}
如何执行这个动作,就需要我们,逐一的找,并辨别我需要的文件,然后提取文件且备份。这一系列的动作,在程序中就可以理解为:遍历,判断,定义容器(变量)并返回。大致意思如下
// 找文件的方法
function 找文件(文件夹){
let 备份 = 新建文件夹;
遍历参数文件夹{
if(实体文件){
备份.添加(遍历中的文件)
}else if(文件夹){
//。。。
}else{
//空
}
}
return 备份 || 空 ;
}
在不考虑当遍历的东西为文件夹时,以上的情况,就好比我们找到了文件,并备份。那么,当我们遍历到了文件夹时,又该如何处理?
重点思考:我们此时回过头来,我们的这个函数是不是就是在做一件事——找实体文件,我们传入的参数是文件夹,返回的是实体文件或为空。此时,再回到遍历的结果为文件夹时,我们依旧是想得到这个文件夹下的实体文件,那么我们的这个函数就刚好可以解决这个要求。
所以我们就可以代码复用,仔细理解下面的伪代码:
else if(遍历的文件夹){
备份.添加(找文件(遍历的文件夹))
}
3.结合实际
上面这段伪代码,即是在递归调用。而结合实际,我们就只是在进入一个子文件夹里面,再次执行找文件的动作。此时,我们结合上面的图片,黑色的线 就表示 “找文件的函数”,圈 就表示 参数 ,而 蓝圈 或者 数字 就表示 遍历的情况。
如图,当我们执行第一次“找文件”的动作:就是在传递红圈(文件夹),当开始找文件时,就需要依次(遍历)来找,此时,遍历出的是一个文件夹。我们此时就需要进入这个文件夹(而其他文件夹此时就先不管,等找完这个文件夹再说),然后,我们就找到了第一个实体文件(1),当找完所有的,就已经没有其他文件了,即会开始return,然后,就会回到上一次还没处理完的遍历的任务进程中。
在递归调用中,我们尽可能的实现了访问每一个对象,在不断层层递进的过程中,当我们到达最深层时,得到了我们所需要的东西后,便逐层往回归来,并处理其他未处理的对象。
总的来说,就是在不断递进,然后在达到一定条件后,又不断归来的一个过程。而这种处理机制,也可以用于对 引用数据类型 的 “深拷贝”
递归函数的使用要注意函数终止条件避免死循环;
递归的实现形式:
1.声明一个命名函数,通过函数名调用
function loop(x){
if(x > = 10)
return;
loop(x + 1);
};
//箭头函数
const loop = x =>{
if(x >= 10)
return;
loop(x + 1)
}
2.5立即调用函数表达式 :(IIFE)是一种被加载到浏览器的编译器之后直接调用的函数,识别IIFE的方法是通过在函数声明的末尾定位额外的左和右括号。
(function foo() {
console.log("Hello Foo");
}());
(function food() {
console.log("Hello Food");
})();
函数的调用模式
1.函数调用形式
一般声明函数后直接调用即是
// 声明一个函数,并调用
function func() {
alert("Hello World");
}
func();
或者:
代码如下:
// 使用函数的Lambda表达式定义函数,然后调用
var func = function() {
alert("你好,程序员");
};
func();
在函数调用模式中,函数里的 this 关键字指全局对象,如果是在浏览器中就是指向window对象
2.方法调用模式
将函数赋值给对象的成员后,那么这个就不在称为函数,而应该叫做方法
// 定义一个函数
var func = function() {
alert("我是一个函数么?");
};
// 将其赋值给一个对象
var o = {};
o.fn = func; // 注意这里不要加圆括号
// 调用
o.fn();
此时,o.fn 则是方法,不是函数了。实际上 fn 的方法体与 func 是一模一样的,但是这里有个微妙的不同。看下面的代码:
代码如下:
// 接上面的代码
alert(o.fn === func);
打印结果是true,这个表明两个函数是一样的东西,但是修改一下函数的代码:
// 修改函数体
var func = function() {
alert(this);
};
var o = {};
o.fn = func;
// 比较
alert(o.fn === func);
// 调用
func();
o.fn();
这里的运行结果是,两个函数是相同的,因此打印结果是 true。但是由于两个函数的调用是不一样的,func 的调用,打印的是 [object Window],而 o.fn 的打印结果是 [object Object]。
这里便是函数调用与方法调用的区别,函数调用中,this 专指全局对象 window,而在方法中 this 专指当前对象,即 o.fn 中的 this 指的就是对象o。
3.构造器调用模式
同样是函数,在单纯的函数模式下,this 表示 window;在对象方法模式下,this 指的是当前对象。除了这两种情况,JavaScript 中函数还可以是构造器。将函数作为构造器来使用的语法就是在函数调用前面加上一个 new 关键字。
/ 定义一个构造函数
var Person = function() {
this.name = "程序员";
this.sayHello = function() {
alert("你好,这里是" + this.name);
};
};
// 调用构造器,创建对象
var p = new Person();
// 使用对象
p.sayHello();
上面的案例首先创建一个构造函数Person,然后使用构造函数创建对象p。这里使用 new 语法。然后在使用对象调用sayHello()方法,这个使用构造函数创建对象的案例比较简单。从案例可以看到,此时 this 指的是对象本身。除了上面简单的使用以外,函数作为构造器还有几个变化,分别为:
1、所有需要由对象使用的属性,必须使用 this 引导;
2、函数的 return 语句意义被改写,如果返回非对象,就返回this。
构造器中的 this
我们需要分析创建对象的过程,方能知道 this 的意义。如下面代码:
代码如下:
var Person = function() {
this.name = "程序员";
};
var p = new Person();
这里首先定义了函数 Person,下面分析一下整个执行:
1、程序在执行到这一句的时候,不会执行函数体,因此 JavaScript 的解释器并不知道这个函数的内容。
2、接下来执行 new 关键字,创建对象,解释器开辟内存,得到对象的引用,将新对象的引用交给函数。
3、紧接着执行函数,将传过来的对象引用交给 this。也就是说,在构造方法中,this 就是刚刚被 new 创建出来的对象。
4、然后为 this 添加成员,也就是为对象添加成员。
5、最后函数结束,返回 this,将 this 交给左边的变量。
分析过构造函数的执行以后,可以得到,构造函数中的 this 就是当前对象。
构造器中的 return
在构造函数中 return 的意义发生了变化,首先如果在构造函数中,如果返回的是一个对象,那么就保留原意。如果返回的是非对象,比如数字、布尔和字符串,那么就返回 this,如果没有 return 语句,那么也返回 this,看下面代码:
代码如下:
// 返回一个对象的 return
var ctr = function() {
this.name = "赵晓虎";
return {
name:"牛亮亮"
};
};
// 创建对象
var p = new ctr();
// 访问name属性
alert(p.name);
执行代码,这里打印的结果是”牛亮亮”。因为构造方法中返回的是一个对象,那么保留 return 的意义,返回内容为 return 后面的对象,再看下面代码:
代码如下:
// 定义返回非对象数据的构造器
var ctr = function() {
this.name = "赵晓虎";
return "牛亮亮";
};
// 创建对象
var p = new ctr();
// 使用
alert(p);
alert(p.name);
代码运行结果是,先弹窗打印[object Object],然后打印”赵晓虎”,因为这里 return 的是一个字符串,属于基本类型,那么这里的 return 语句无效,返回的是 this 对象,因此第一个打印的是[object Object]而第二个不会打印 undefined。
4.apply调用模式
除了上述三种调用模式以外,函数作为对象还有 apply 方法与 call 方法可以使用,这便是第四种调用模式,我称其为 apply 模式。
首先介绍 apply 模式,首先这里 apply 模式既可以像函数一样使用,也可以像方法一样使用,可以说是一个灵活的使用方法。首先看看语法:函数名.apply(对象, 参数数组);
1、新建两个 js 文件,分别为”js1.js”与”js2.js”;
2、添加代码
代码如下:
// js1.js 文件中
var func1 = function() {
this.name = "程序员";
};
func1.apply(null);
alert(name);
// js2.js 文件
var func2 = function() {
this.name = "程序员";
};
var o = {};
func2.apply(o);
alert(o.name);
3、分别运行着两段代码,可以发现第一个文件中的 name 属性已经加载到全局对象 window 中; 而第二个文件中的 name 属性是在传入的对象 o 中,即第一个相当于函数调用,第二个相当 于方法调用。
这里的参数是方法本身所带的参数,但是需要用数组的形式存储在,比如代码:
代码如下:
// 一个数组的例子
var arr1 = [1,2,3,[4,5],[6,7,8]];
// 将其展开
var arr2 = arr1.conact.apply([], arr1);
然后介绍一下 call 模式,call 模式与 apply 模式最大的不同在于 call 中的参数不用数组,看下面代码就清楚了:
// 定义方法
var func = function(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
};
// 创建对象
var o = {};
// 给对象添加成员
// apply 模式
var p1 = func.apply(o, ["赵晓虎", 19, "男"]);
// call 模式
var p2 = func.call(o, "赵晓虎", 19, "男");
上面的代码,apply 模式与 call 模式的结果是一样的。
实际上,使用 apply 模式和 call 模式,可以任意的操作控制 this 的意义,在函数 js 的设 计模式中使用广泛。简单小结一下,js 中的函数调用有四种模式,分别是:函数式、方法式、构造 器式和 apply 式. 而这些模式中,this 的含义分别为:在函数中 this 是全局对象 window,在方 法中 this 指当前对象,在构造函数中 this 是被创建的对象,在 apply 模式中 this 可以随意的指定.。在 apply 模式中如果使用 null,就是函数模式,如果使用对象,就是方法模式。
当函数没有返回值,但却用变量接收了,这个变量的值是undefined
function foo(a,b){
a + b;
}
let c = foo(1,2,3)
console.log(c)//undefined
当函数没有明确返回值时,接收函数返回值的变量为undefined
function foo(a,b){
a + b;
return;
}
let c = foo(1,2,3)
console.log(c)//undefined
函数作为参数使用:一个函数作为参数传递。此函数称为回调函数
function f1(fn){
console.log('i love you')
fn()
};
function f2(){
console.log('keep the time')
}
f1(f2)
//i love you
//keep the time
函数作为返回值
function f1(){
console.log('f1');
return function(){
console.log('f2')
};
};
f2 = f1();
f2();
//f1
//f2
预解析
当你在变量未声明或者函数没有声明前,使用变量或者函数,则会产生预解析,将变量的声明和函数的声明提前到你使用的变量或者函数的作用域的最上面。
console.log(num)
var num = 10;
//等价于
var num;
console.log(num)
num = 10;
f1();
function f1(){
console.log('f1')
}
//等价于
function f1(){
console.log('f1')
}
f1();
原始值和引用值的区别
1)原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。
2)引用值:对象变量它里面的值是这个对象在堆内存中的内存地址,这一点你要时刻铭记在心!
因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因了,因为它们都指向同一个对象。
3.循环、分支、判断
4.函数、作用域、this
WebAPI
1.DOM
2.BOM
3.基于XML HttpRequest的Ajax操作
jquery的核心是什么?
链式编程,隐式迭代