一. 数据类型
值类型:Number,String,Boolean,undefined,null
引用类型:array,object,function
1、判断数据类型typeof
typeof可以返回6个结果:number、string、boolean、object、undefined、function
console.log(typeof(aaa));//"undefined"
console.log(typeof(typeof(aaa)));//string
console.log(typeof(undefined));//undefined
console.log(typeof(NaN));//number
console.log(typeof(null));//object
console.log(typeof(function(){}));//function
console.log(typeof([]));//object
console.log(typeof({}));//object
console.log(typeof(123));//number
console.log(typeof(new Number(123)));//object
var a='123abc';
console.log(typeof(+a));//number
console.log(typeof(!!a));//boolean
console.log(typeof(a+""));//string
console.log('11'+11);//1111
console.log(parseInt('123abc'));//123
var num = 123123.345789;
console.log(num.toFixed(3));//123123.346
2、数据类型的显示转换
console.log(Number(null));//0
console.log(Number('a'));//NaN
console.log(Number(undefined));//NaN
console.log(parseInt(true));//NaN
3、数据类型的隐示转换
1. isNaN
2. ++/-- +/-
3. +
4. */%
5. && || !
6. < > <= >=
7. == !=
其中1-5调用显式类型里面的Number();
6-7调用显式类型的Boolean();
其中不发生类型转换的有:
===:绝对等于
!==:绝对不等于
4、特殊情况
NaN是非数,不等于任何。
练习题一
var str = false + 1 ;
console.log(str);//1
var demo = false == 1;
console.log(demo);//false
if(typeof(a) && -true + (+undefined) + "" ){
console.log('基础扎实');//基础扎实
}
if(11 + "11" * 2 == 33){
console.log('基础扎实');//基础扎实
}
!!" " + !!"" - !!false||console.log('哈哈哈');//不打印
//" " 空格字符串,boolean值为true
//"" 空串,boolean值为false
二. 函数
google新版本if里面不能定义函数,IE8可以
练习题一,形参、实参
function sum(a,b){//形参
if(arguments.length>sum.length){
console.log('实参多了');
}
else if(arguments.length<sum.length){
console.log('形参多了');
}
else{
console.log('相等');
}
}
sum(1,2,3);//实参
练习题二
function b(x, y, a){
arguments[2] = 10;//a=10
console.log(a);
}
b(1,2,3);
1、立即执行函数
立即执行函数:函数执行过后立即被销毁
(function (){}());//W3C推荐
(function (){})();
针对初始化功能的函数,可以有参数。
var num = function (a,b){
return a + b;
}(1,2);
(function abc(){
var a = 123;
var b = 234;
console.log(a+b);
}())
只有表达式才能被执行符号执行,能被执行符号执行的表达式,函数名字会被自动忽略。
function test(){
console.log("a");
}() //会出现语法解析错误,因为括号前面是函数声明
(+ function test( ){
console.log('a');
}()) //打印出a
// 阿里面试题
function test(a, b, c, d){
console.log(a + b + c + d);
}(1, 2, 3, 4);
// 不报错也没有执行
2、递归
递归有两个重要的步骤:1、找规律; 2、找出口
典型示例:斐波那契额额数列:1 1 2 3 5 8
递归
3、arguments.callee指向函数自身引用
区别 func.caller
三、预编译
1、全局变量
①.imply global暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有
var a=b=3;
window.a==》没有
window.b==》有
②.一切声明的全局变量,全是window的属性
全局的var a=10;==》window.a=10;
window就是全局的域
全局执行期上下文:生成了一个GO对象
Global Object
GO === window
一旦经历了var的操作,所得出的属性(window),这种属性叫做不可配置的属性
不可配置的属性delete不掉
2、预编译
js运行三部曲:语法分析、预编译、解释执行
预编译发生在函数执行的前一刻
函数声明整体提升
变量 声明提升
3、预编译四部曲
1.创建AO对象(Activation Object)(执行期上下文) GO==window
2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
3.将实参值和形参统一
4.在函数体里面找函数声明,值赋予函数体
PS:先生成GO,再生成AO,如果AO、GO都有某个属性,优先取AO的
练习题一
function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a(){}//函数声明
console.log(a);
var b = function(){}//函数表达式
console.log(b);
function d(){}
}
fn();
练习题二
function test(a,b){
console.log(a);
c=0;
var c;
a=3;
b=2;
console.log(b);
function b(){}
function d(){}
console.log(b);
}
test(1);
第一步:
AO{
}
第二步:
AO{
a: undefined,
b: undefined,
c: undefined
}
第三步:
AO{
a: 1,
b: undefined,
c: undefined
}
第四步:
AO{
a: 1,
b: function b(){},
c: undefined,
d: function d(){}
}
//输出1 、 2、 2
四、作用域、作用域链
1、基础概念
- [[scope]]:每个JavaScript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供JavaScript引擎存取,[[scope]]就是其中一个。
[[scope]]指的就是我们所说的作用域,其中存储了执行期上下文的集合。- 作用域链:[[scope]] 中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链接叫做作用域链。
- 运行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象(AO)。一个执行期上下文定义了一个函数执行的环境,函数每次执行时对应的执行环境都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
- 查找变量:从作用域链的顶端依次向下查找。
function a(){
function b(){
function c(){
}
c();
}
b();
}
a();
a defined a.[[scope]] ----> 0 : GO //a定义的时候产生GO对象
a doing a.[[scope]] ----> 0 : aAO //a执行的时候新产生AO对象
1 : GO
b defined b.[[scope]] ----> 0 : aAO //子级b定义会继承父级a运行时产生的对象
1 : GO
b doing b.[[scope]] ----> 0 : bAO //子级b新产生AO对象
1 : aAO
2 : GO
c defined c.[[scope]] ----> 0 : bAO //c定义时会继承b运行时产生的属性
1 : aAO
2 : GO
c doing c.[[scope]] ----> 0 : cAO //c执行时同时又产生新的AO
1 : bAO
2 : aAO
3 : GO
五、闭包
1、现象
当内部函数被保存到外部时,将会生成闭包,闭包会导致原有作用域链不释放,造成内存泄漏
2、触发情况
两个或多个函数互相嵌套,把里面的函数保存到外部,这样的情况一定会产生闭包。从外面还可以调用里面的函数。
3、作用
1.实现公有变量,eg:函数累加器
2.可以做缓存,存储结构,eg:eater
3.可以实现封装,属性私有化,eg:Person()
4.模块化开发,防止污染全局变量
练习题一
function a(){
function b(){
var bbb = 234;
console.log(aaa);
}
var aaa = 123;
return b;
}
var glob = 100;
var demo = a();
demo();
练习题二
function a1(){
var num=100;
function b(){
num++;
console.log(num);
}
return b;
}
var a111=a1();
a111();
a111();
练习题三
function test(){
var arr = [];
for(var i = 0 ; i < 10 ; i++){
// 输出10个10
arr[i] = function(){
console.log(i+"、");
}
// 采用立即执行函数修改代码后,输出0~9
/*(function (j){
arr[j] = function(){
console.log(j+"、");
}
}(i));*/
}
return arr;
}
var myArr = test();
for(var j = 0 ; j < myArr.length ; j++){
myArr[j]();
}
练习题四,私有化变量
function Person(name,age,sex){
var a=0;
this.name=name;
this.age=age;
this.sex=sex;
function sss(){
a++;
console.log(a);
}
this.say=sss;
}
var oPerson = new Person();
oPerson.say();//1
oPerson.say();//2
var oPerson1 = new Person();
oPerson1.say();//1
六、对象
1、对象的创建方法
- var obj = {},plainObject,对象字面量/对象直接量
- 构造函数
1)系统自带的构造函数,new Object()
2)自定义,大驼峰命名规则- Object.create(原型, definedProperty特性);//可读不可写特性
2、构造函数的内部原理
- 在函数体最前面隐式的加上var this = {};
- 执行this.xxx=yyy;
- 隐示的返回this
七、包装类
原始值没有属性和方法
1、其一:String
var str = 'abcd';
console.log(str.length);//new String('abcd').length;
2、其二:Number
var num = 123;
num.name='abc';//new Number(123).name='abc';-->delete
console.log(num.name);//undefined
练习题一
var arr=[1,2,3,4];
arr.length=2;//设置length后会截断
console.log(arr)
var str = "abcd";
// new String('abcd').length=2;-->delete
str.length = 2;
console.log(str);
console.log(str.length);
练习题二
var str='abc';
str += 1;
var test = typeof(str);
if(test.length==6){
// new String(test).sign='';
test.sign='typeof的返回结果可能为String';
}
// new String(test).sign
console.log(test.sign);
八、原型
知识点
prototype:原型,原型是系统内部的隐式属性,只能是系统给了,我们自己添不能用。
prototype{}:是祖先
- 定义:原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
- 利用原型的特点和概念,可以提取共有属性。
- 对象如何查看原型:隐式属性__proto__
- 对象如何查看对象的构造函数:constructor
练习题一
Person.prototype.name = "sunny";
function Person(){
}
var person = new Person();
Person.prototype.name = "cherry";
console.log(person.name);
上述代码输出:cherry
Person.prototype.name = "sunny";
function Person(){
}
var person = new Person();
Person.prototype = {
name: 'cherry'
};
console.log(person.name);
上述代码输出:sunny
Person.prototype.name = "sunny";
function Person(){
}
Person.prototype = {
name: 'cherry'
};
var person = new Person();
console.log(person.name);
上述代码输出:cherry
九、原型链
- 如何构成原型链,示例:grand,father,son
- 原型链上属性的增删改查,与原型相似,就近原则,只有本人具有增删改查的权限,不能通过子孙进行操作。
- Object.create(原型),也可以构造对象。
- 所有对象的最终都会继承自Object.prototype,这句话是错误的,如Object.create(null)就不会。
十、call和apply
this指向是,谁调用的这个方法,this就指向谁
作用:改变this指向
区别:传参形式不同, call需要把实参按照形参的个数传进去
apply需要传一个arguments数组列表
十一、继承(extends或inherit)
//圣杯模式
/*function inherit(Target, Origin){
function F(){};
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin.prototype;
}*/
//上面是基本写法,可以引申出下面这种高级的写法
var inherit = (function(){
var F = function (){};
return function(Target, Origin){
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin.prototype;
}
}());
Father.prototype.lastName = '张三';
function Father(){
}
function Son(){
}
//son继承father的lastName属性,并且给son添加age属性不会影响father
inherit(Son, Father);
var son = new Son();
var father = new Father();
son.age=18;
console.log(son.lastName+":"+son.age);
console.log(father.lastName+":"+father.age);
十二、this
- 函数预编译过程this指向window
- 全局作用域里this指向window
- call/apply可以改变函数运行时this指向
- obj.func();这里指向obj,谁调用的this指向谁
练习题一
var name = "222";
var a = {
name: '111',
say: function(){
console.log(this.name);
}
}
var fun = a.say;
fun();//22
a.say();//111
var b = {
name: '333',
say: function(){
fun();
}
}
b.say(a.say);//222
b.say = a.say;
b.say();//333
练习题二
var foo = '123';
function print(){
var foo = '456';
this.foo = '789';
console.log(foo);
}
print();//456
练习题三
var foo = 123;
function print(){
this.foo = 234;
console.log(foo);
}
print();//234
//new print();//123
练习题四
var a=5;
function test(){
a=0;
console.log(a);
console.log(this.a);
var a;
console.log(a);
}
//执行test()和new test()分别输出什么?
//test();// 0 5 0
new test();// 0 undefined 0
十三、数组,散列模式
1、定义方式
- [] 字面量,推荐
- new Array()
2、sort实现排序
- 必须写俩形参
- 看返回值:当返回值为负数时,那么前面的数在前
当返回值为正数时,那么后面的数在前
为0时,不动
var arr = [20,2,10,13,4,8,9];
arr.sort(function (a, b){
return a-b;//升序
//return b-a;//降序
});
console.log(arr);
十四、类数组:是对象,可以当数组使用
- 可以利用属性名模拟数组的特性
- 可以动态的增长length属性
- 如果强行让类数组调用push方法,则会根据length属性值的位置进行属性的扩充
类数组所具备的特性:
- 属性要为索引(数字)属性
- 必须有length属性,最好加上push()
- 最好有splice()
var obj = {
'2': 'a',
'3': 'b',
'length': 2,
'push': Array.prototype.push
}
//push时会执行下面的push方法
obj.push('c');//obj[2]=c;length++
obj.push('d');//obj[3]=d;length++
console.log(obj);//obj[2]=c,obj[3]=d,length=4
/*Array.prototype.push = function(target){
obj[obj.length] = target;
obj.length ++;
}*/
十五、其它小知识点
1、运算符优先级:=最弱,()优先级较高
2、逗号操作符,返回逗号后面的
var a = (1-1, 1+1);
console.log(a);
var f=(
function f(){
return "1";
},
function g(){
return 2;
}
)();
console.log(typeof(f));
3、JS精度不准,可正常计算的范围(小数点前16位,后16位)
console.log(0.14*100); // 结果不是14
生成随机数时可以用向上取整/向下取整来避免这个问题
4、未经申明的变量,只有放到typeof才不报错
var x=1;
if(function f(){}){
//f在这里是未经申明的变量,所以为undefined
x += typeof f;
}
//输出结果为:1undefined
console.log(x);
5、Error.name的六种值对应的信息
- EvalError:eval()的使用与 不一致
- RangeError:数值越界
- ReferenceError:非法或不能识别的引用数值
- SyntaxError:发送语法解析错误
- TypeError:操作数据类型错误
- URIError:URI处理函数使用不当
6、ES5.0严格模式:use strict
现在的浏览器基于es3.0和es5.0新增的部分,如果两者冲突默认使用es3.0
两种用法:全局严格模式
局部函数内严格模式(推荐)
“use strict”:不再兼容es3的一些不规则语法,使用全新的es5规范
- 不支持with,arguments,callee,func,caller
- 变量赋值前必须声明
- 局部的this必须赋值,赋值什么就是什么
- 拒绝重复属性和参数
7、命名空间
作用:管理变量,防止污染全局,适用于模块化开发。
命名空间用到的技术:
1、立即执行函数,执行过后立即销毁,不会污染全局变量。
2、闭包,产生私有化变量,不会污染全局;还可以把我们还要适用的内部方法保留到外部,来实现一些方法的调用。
8、属性表示方法
obj.name等同于obj[‘name’];
obj.name效率慢,需要先转换为obj[‘name’];
9、对象的枚举enumeration
for in 遍历
- hasOwnProperty:可以过滤原型上的属性
- instanceof:看A对象的原型链上有没有B的原型
- in