1.简介
JavaScript的数据类型共有7种:
基本数据类型六种:
数值(number)
字符串(string)
布尔值(boolean)
undefined
null
symbol
引用数据类型一种:
对象(object array function)
基本类型的值放在栈区的(栈区指内存里的栈内存)
引用类型的值同时保存在栈内存和堆内存的对象中,栈区内存保存变量标识符和指向堆内存中该对象的指针
2.运算符
JavaScript确定一个值到底是什么类型有下列几种方式
(1)typeof操作符可以返回一个值的数据类型,可以不加括号,主要有7种:number string boolean undefined(数据未被声明或者未初始化) object(null或者对象) symbol和函数类型 function
(2)Instanceof运算符可以区分数组和对象 [] instanceof Array; //true
(3)区分对象,数组和null Object.prototype.toString.call(null) //[object Null]
(4)Array.isArray(arr) //判断数组
(5)constructor也能判断类型,undefined和null没有这个属性
'[]'.constructor==Array
3.数据类型
undefined==null //true Number(null) //0 5+null //5 Number(undefined) //NaN 5+undefined //NaN
Null与undefined的区别是:null是一个表示“空”的对象,转为数值时为0;undefined是一个表示“此处无定义”的原始值,转为数值时为NaN.
undefined null false 0 NaN “” ‘’ 被转为false,其他值都视为true
空数组([])和空对象({})对应的布尔值都是true
整数和浮点数:1===1.0//true 0.1+0.2===0.3//false 0.3/0.1 //2.999999 (0.3-0.2)===(0.2-0.1) //false
0.1+0.2!=0.3的原因:
这是使用基于IEEE754数值的浮点计算的通病,浮点数值的最高精度是17位小数,进行算数运算时其精确度远远不如整数,存在舍入误差,其结果为0.30000000000000004
解决方式:Math.abs(0.1+0.2-0.3)<Number.EPSILON
数值的表示法:
十进制:没有前导0的数值
八进制:有前缀0o或00的数值,或者有前导0,且只用到0-7的八个阿拉伯数字的数值。
十六进制:有前缀0x或0X的数值。
二进制:有前缀0b或0B的数值。
几乎所有的场合正零和负零都会被当做正常的0,唯一有区别的场合是,+0和-0当做分母,返回的值是不相等的。除以正零得到+Infinity,除以负零得到-Infinity,这两者是不相等的。
NaN表示“非数字”,主要出现在将字符串解析成数字出错的场合
5-‘x’ //NaN 0/0 //NaN typeof NaN //’number’
NaN不等于任何值,包括它本身,数组的indexOf方法内部使用的是严格相等运算符,所以[NaN].indexOf(NaN) //-1
NaN在布尔运算时被当做false。Boolean(NaN) //false
NaN与任何数(包括它自己)的运算,得到的都是NaN
Boolean([]); //true
Number([]); //0
Number({}); // NaN
Number(false); //0
任何对象转为布尔值,都为得到 true(切记!在 JS 中,只有 0,-0,NaN,"",null,undefined 这六个值转布尔值时,结果为 false)
4.与数值相关的全部方法
parseInt() 用于将字符串转为整数
parseFloat() 用于将一个字符串转为浮点数
isNaN方法可以用来判断一个值是否为NaN
判断NaN的方法:
function myisNaN(value){
return typeof value===’number’ && isNaN(value);
}
function myisNaN(value){
return value !== value;
}
isFinite方法返回一个布尔值,表示某个值是否为正常的数值。
isFinite(Infinity) //false
isFinite(-Infinity) //false
isFinite(NaN) //false
isFinite(undefined) //false
isFinite(null) //true
isFinite(-1) //true
js中不改变原来数组对象的方法
map concat slice filter,forEach,some,every
js中改变原数组的方法:
pop push shift sort splice unshift
丢弃小数部分,保留整数部分parseInt(7/2)
向上取整,有小数就整数部分加1 Math.ceil(7/2)
四舍五入math.round(7/2)
向下取整Math.floor(7/2)
取几位小数:Math.toFixed()
5.字符串
字符串与数组的相似性仅此而已,实际上无法改变字符串之中的单个字符,字符串内部的单个字符无法改变和增删,这些操作会默默地失败。
length属性返回字符串的长度,该属性也是无法改变的
6.对象
对象是一组“键值对”的集合,是一种无序的复合数据集合
对象的引用:如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量,如果取消某个变量对于原对象的引用,不会影响到另一个变量。
属性的操作:
读取对象的属性有两种方法:一种是使用点运算符,另一种是使用方括号运算符
方括号运算符内部可以使用表达式,数值键名不能使用点运算符(因为会被当做小数点)。
查看一个对象本身的所有属性:可以使用Object.keys(obj)方法
属性的删除:delete命令delete obj.p //true
Delete命令只能删除对象本身的属性,无法删除继承的属性。
属性是否存在:in运算符用于检查对象是否包含某个属性,如果包含就返回true,否则就返回false。‘toString’ in obj //true
可以使用对象的hasOwnProperty方法判断一下,是否为对象自身的属性。obj.hasOwnProperty(‘toString’) //false
属性的遍历:for…in循环
for…in循环用来遍历一个对象的全部属性。
7.with语句
操作同一个对象的多个属性,提供一些书写的方便
var obj = {p1:1,p2:2,};
with(obj) { p1 =4; p2 =5;}
// 等同于
obj.p1 =4;
obj.p2 =5;
如果width区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量
var obj = {};
with(obj) { p1 =4; p2 =5;}
obj.p1// undefined
p1// 4
8.函数
三种声明函数的方式:
function命令:function(){}
函数表达式:var s=function(){}
Function构造函数:var add =new Function('x','y','return x + y');
函数的重复声明:如果同一个函数被多次声明,后面的声明就会覆盖前面的声明
函数名的提升:采用function声明函数时,整个函数会像变量声明一样,被提升到代码头部,也就是在调用之前已经声明了。
如果同时采用function命令和赋值语句声明同一个函数,采用赋值语句的定义。
函数的属性:
name属性 返回函数名字
length属性 返回函数定义时的参数个数
函数的toString()方法返回函数的源码
函数参数,如果是原始类型的值,传递方式是传值传递,在函数体内修改参数值,不会影响函数外部;如果是复合类型的值(数组、对象、其他函数),传递方式是传址传递,修改函数体内的参数值会影响函数外部。注意:如果函数体内修改的不是对象的属性而是替换了整个对象的值,不会影响原始值。
arguments对象的长度由传入的参数个数决定的,不是由定义时参数的个数决定的。arguments对象可以读取函数所有的对象,arguments的callee属性,返回他所对应的原函数,可以通过这个调用自身,但是严格模式下不能使用。
函数声明与函数表达式的区别
函数声明在JS解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用。而函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用。
9.执行环境
定义了变量或函数有权访问的其他数据,决定了他们各自的行为,每个执行环境都有一个与之关联的变量对象。
10.作用域、作用域链
作用域:
(1)全局作用域
任何地方都能访问到的的对象有全局作用域,函数外部定义的变量、未定义直接赋值的变量、window对象的属性
(2)局部作用域
函数内部
(3)块级作用域
let const声明,在指定块作用域外部不能访问。
函数本身也有自己的作用域,它的作用域与变量一样,就是函数声明时所在的作用域,与函数运行时的作用域无关。
作用域链:
(1)当执行函数时,总是先从函数内部找寻局部变量
(2)如果内部找不到(函数的局部作用域没有),则会向创建函数的作用域(声明函数的作用域)寻找,依次向上,直到查到全局作用域,这个查找过程形成的链条就叫作用域链。
11.垃圾收集
JavaScript具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存。
原理:找出不再使用的变量,释放其内存。
标记清除、
引用计数:存在循环引用问题,可以通过解除引用来解决。
12.数组
typeof运算符会返回数组的类型是object
Object.keys(arr)返回数组的所有键名
length属性返回数组的成员数量,该属性是一个动态的值,等于键名中的最大整数加上1
in运算符检测某个键名是否存在,适用于对象也适用于数组
for...in循环和数组的遍历 数组遍历一般使用for while forEach
两个逗号之间没有任何值,称该数组存在空位
使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历,空位都会被跳过。
数组的slice可以将“类似数组的对象arrayLike”转换成数组 Array.prototype.slice.call(arrayLike)
数组的迭代方法:
every(){ }对数组中的每一项运行给定函数,如果每一项都返回true,则返回true
filter(){}对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组
forEach(){}对数组中的每一项运行给定函数,没有返回值
map(){}对数组中的每一项运行给定函数,返回函数调用结果组成的数组
some(){}对数组中的每一项运行给定函数,如果有一项返回true,则返回true
reduce()从数组的第一位开始,遍历到最后一位
reduceRight()从数组的最后一位开始,向前遍历
13.基本包装类型
Boolean String Number
生存期:只存在一行的代码执行瞬间,然后立即被销毁
14.Number()
将任意类型的数据转换成数值,Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。
parseInt ('42 cats')// 42
Number ('42 cats')// NaN
15.String()
将任意类型的数据转化成字符串,String方法转换规则,先调用toString,再调用valueOf,与Number方法调用顺序相反
charAt charCodeAt fromCharCode
16.Boolean()
将任意类型的值转换成布尔类型,除了以下五种boolean值为false,其他的都为true
undefined null 0(包含-0和+0) NaN ''(空字符串)
所有对象的布尔值都为true
17.创建对象
工厂模式
创建对象解决了创建多个相似对象的问题,没有解决对象识别问题(怎样知道一个对象的类型)
function createperson(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayname=function(){
return this.name;
}
return o;
}
var person1=createperson('haha',5,'teacher');
构造函数模式
可以创建特定类型的对象,但是每定义一个实例都有方法,且方法不是同一个Function的实例
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayname=function(){
return this.name;
}
}
var person1=new Person('haha',5,'teacher');
原型模式
使用原型对象可以让所有实例共享它所包含的属性和方法。
1.对象都有属性__proto__,指向该对象的构造函数的原型对象。函数方法也是一种对象
2.方法除了有属性__proto__,还有属性prototype,prototype指向该方法的原型对象。
hasOwnProperty()判断一个属性存在于实例中还是原型对象中
取一个对象上所有可枚举的实例属性 Object.keys()
想要得到所有实例属性 Object.getOwnPropertyNames()
原型模式的问题:由其共享性导致的,尤其是包含引用类型值时。
组合使用原型模式和构造函数模式
function persionc(name,age){
this.name=name;
this.age=age;
}
persionc.prototype={
constructor:persionc,
sayname:function(){
alert(this.name);
}
}
寄生构造函数模式
除了用new新建实例外,跟工厂模式一模一样
适用于想创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数,就用这种模式
18.继承
方法一
实现继承,通过原型链来实现
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的constructor指针,而实例都包含一个指向构造函数原型对象的内部指针,让子类原型对象等于父类的实例,此时,子类原型对象将包含一个指向父类原型的指针。如果父类原型又是另一个类型的实例,如此层层递进,就构成实例与原型的链条,这就是所谓原型链的概念。
//实现继承
function supertype(){
this.superval=true;
}
supertype.prototype.getsuper=function(){
console.log(this.superval);
}
function subtype(){
this.subval=false;
}
subtype.prototype=new supertype();
subtype.prototype.getsub=function(){
console.log(this.subval);
}
var a=new subtype();
原型链的问题:(1)包含引用类型值的原型属性会被所有实例共享
(2)不能在不影响所有对象实例的情况下,向超类构造函数传递参数
方法二
借用构造函数技术
原理:在子类构造函数内部调用超类构造函数
function sup(name){
this.name=name;
this.color=['red','blue'];
}
function sub(){
sup.call(this,'haha');
}
var a=new sub();
var b=new sub();
a.color.push('pink');
console.log(a.color);
console.log(b.color);
console.log(b.name);
问题:函数复用无从谈起
方法三
组合原型继承和构造函数
原理:通过原型对象实现对原型属性和原型方法的继承,通过构造函数实现实例属性的继承。
function sup1(name){
this.name=name;
this.colors=['red','blue'];
}
sup1.prototype.getname=function(){
console.log(this.name);
}
function sub1(name,age){
sup1.call(this,name);
this.age=age;
}
sub1.prototype=new sup1();
sub1.prototype.constructor=sub1;
sub1.prototype.getage=function(){
console.log(this.age);
}
var instance1=new sub1('a',4);
var instance2=new sub1('b',8);
instance1.colors.push('pink');
console.log(instance1.colors);
console.log(instance2.colors);
instance1.getname();
instance2.getname();
instance1.getage();
instance2.getage();
19.函数表达式
(1)定义函数的方式,递归
函数声明、函数表达式
arguments.callee是一个指向正在执行的函数的指针
function factorial(num){
if(num<=1) return 1;
else return num*factorial(num-1);
}
function factorial1(num){
if(num<=1) return 1;
else return num*arguments.callee(num-1);
}
var factorial2=(function f(num){
if(num<=1) return 1;
else return num*f(num-1);
})
console.log(factorial(5));
console.log(factorial1(5));
console.log(factorial2(5));
(2)闭包
闭包会携带包含它的函数的作用域,所以会比其他函数占用更多的内存,过度使用闭包会导致内存占用过度。
function f1 ()
{
var n =999;
function f2 ()
{
console.log(n);
}
return f2;
}
var result = f1();
result();// 999
闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
立即调用函数表达式:
(function(){/* code */}());
// 或者(function(){/* code */})();
20.间歇调用和超时调用
超时调用
var timeout=setTimeout(function(){
alert('hi');
},1000);
取消超时调用
clearTimeout(timeout);
间歇调用
var interval=setInterval(function(){
alert('hi')
},1000);
//取消间歇调用
clearInterval(interval)
13.加法运算
如果运算子是对象,必须先转成原始类型的值,然后再相加。规则如下所示:首先,自动调用对象的valueOf方法,一般来说,对象的valueOf方法总是返回对象自身,这时再自动调用对象的toString方法,将其转为字符串。 但是如果运算子是一个Date对象的实例,那么会优先执行toString方法。
14.余数运算符
运算结果的正负号由第一个运算子的正负号决定
15.数值运算符+
将任何值转换成数值
16.指数运算符**
右结合,先进行最右边的计算
17.相等运算符
+0===-0
对于两个对象的比较,严格相等运算符比较的是地址,而大于或小于运算符比较的是值。
==
原始类型比较会先转换成数值再进行比较
对象与原始类型值比较
对象与数值比较时,对象转换成数值
对象与字符串比较时,对象转换成字符串
对象与布尔值比较时,两边都转换成数值
undefined与null和其他类型相比时,结果都为false,它们互相比较时结果为true
18.取反运算符!
不管什么类型的值,经过取反运算,都变成布尔值
19.且运算符 &&
如果第一个运算子的布尔值为true,则返回第二个运算子的值(注意是值,不是布尔值);如果第一个运算子的布尔值为false,则直接返回第一个运算子的值,且不再对第二个运算子求值。
20.或运算符||
如果第一个运算子的布尔值为true,则返回第一个运算子的值,且不再对第二个运算子求值;如果第一个运算子的布尔值为false,则返回第二个运算子的值。
21.二进制或运算符
位运算只对整数有效,遇到小数时,会将小数部分舍去,只保留整数部分。所以,将一个小数与0进行二进制或运算,等同于对该数去除小数部分,即取整数位。
22.异或运算符
“异或运算”有一个特殊运用,连续对两个数a和b进行三次异或运算,a^=b; b^=a; a^=b;,可以互换它们的值。这意味着,使用“异或运算”可以在不引入临时变量的前提下,互换两个变量的值。
23.void运算符
执行一个表达式,不返回任何值,或者说返回undefined
24.右结合的运算符
赋值运算符 三元运算符 指数运算符
28.js获取URL参数的方法
split拆分法
function request(){
var url=location.search;
var request=new Object();
if(url.indexOf("?")!=-1){
var str=url.str(1);
var strs=str.split("&");
for(var i=0;i<strs.length;i++){
requst[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
}
}
return request;
}
29.面向对象——设计一个超市对象
一家超市,一个出口,一个入口,多个收银台,可以提供微信、支付宝等支付手段
30数组降维
return Array.prototype.concat.apply([ ],arr);
31.伪数组转换为数组
var arr = Array.prototype.slice.call(oldarr);
var arr=Array.from(oldarr);
32代码回收规则如下:
(1)全局变量不会被回收。
(2)局部变量会被回收,也就是函数一旦运行完以后,函数内部的东西都会被销毁。
(3)只要被另外一个作用域所引用就不会被回收
33.正则表达式:^ 以什么开头,$以什么结尾;*任意多次;|或的意思;()组;[]集合