目录
数据类型
-
分类
- 基本(值)类型
- string: 任意字符串
- number: 任意数字
- boolean: true false
- undefined: undefined
- null: null
- 对象(引用)类型
- object: 任意对象
- function: 一个特别的对象(可以执行)
- array: 一种特别的对象(属性有数值下标属性,数组内部的元素是有序的)
- 基本(值)类型
-
判断
- typeof: 返回数据类型的字符串表达
typeof tmp==="undefined"
,array和object的typeof都是object所以不能区别 - instanceof: 实例
- =/ 一般判断全等用===这个不做数据转化
a===undefined
一般用于null undefined的判断(因为他们只有一个值)
- typeof: 返回数据类型的字符串表达
-
undefined与null区别
- undefined 表示只有定义没有赋值
- null 表示定义了赋值了值为null
- 设置null的初衷是为了表示这个变量准备被赋值成对象,但是目前还没有赋值,所以
typeof null
的结果是object - null的另一个作用是将没有用的对象的地址设置为null,当内存没人指的时候就会释放,被垃圾回收器回收
-
在什么时候将变量的值赋值为null
- 在初始化但是临时不赋值的时候,为了标记
- 在变量不用的时候释放内存
-
严格区分数据类型与变量类型
- 数据的类型
- 基本类型
- 对象类型
- 变量类型(实际上指的是变量值的类型,是存了数字还是地址)
- 基本类型: 保存的是基本类型数据
- 引用类型: 保存的是地址的路径
- 数据的类型
数据,变量,与内存
- 数据: 存储在内存中代表特定信息的东西,本质上是
010101..
- 内存: 内存条通电之后可以存储数据的空间(临时的),一个内存块里面有两个小数据一个是地址值一个是存的值
- 内存的分类有栈,堆
- 栈里面存全局变量和局部变量
- 堆里面是对象的本身(但是表示堆的标识符是在堆空间里面的)
- 变量: 可变换的量,由变量名和值组成,每个变量名都占用一小块内存
内存中所有的操作的目标都是数据,操作的运算有:
-
算数运算
-
逻辑运算
-
赋值运算
-
运行函数
-
数据,变量,内存之间的关系
内存是存储数据的临时空间,变量是内存的标识
- 内存与赋值的问题
var a=XXX;
a中到底存了什么?
- 当XXX是基本数据类型,那么他保存的就是这个数据
- 当XXX是对象的时候,保存的是对象的地址值
- 当XXX是变量的时候,保存的是XXX的内存内容(两者都有可能)
- 当两个引用变量指向了同一个对象,通过一个变量修改数据,那么另一个变量看到的是修改后的数据
- 对象在函数传递参数的时候是将内容赋值给了形参,对象赋值的内容是地址,所以相当于是传地址
- 值得注意这两种情况
var a={
age:12};
var b=a;
a={
age:13};
// 此时ab指向的地址发生了改变,两者不相干
var fun=function(v){
v={
age:15};
}
fun(b);
// 此时b并不会发生改变因为v是一个**独立的变量**,最开始是和b指向了一个地址,但是赋值之后指向的地址发生了改变,所以v的变化不会影响b
- JS是如何管理内存的
- 内存生命周期
- 分配小的内存空间,获得小内存的使用权
- 存储数据进行反复操作
- 没人指向小内存空间的时候释放空间
- 释放内存
- 程序或者作用域结束的时候释放作用域里面的变量(注意这里指的是释放标识符,小内存早就被释放了)
- 对于对象使用垃圾回收器回收
对象
- 什么是对象
- 多个数据的封装体
- 用来保存多个数据的容器
- 一个对象代表现实中的一个事物
- 为什么要用对象
- 统一管理多个元素
- 对象的组成
- 属性
- 方法(特别的属性–属性值是函数)
- 访问对象内部的元素
OBJ.属性名
当属性名是关键字/包含-
/包含/变量名不确定,用变量存储变量名的时候不能用
OBJ["属性名"]
函数
- 为什么要用函数
- 提高代码复用
- 如何定义函数
- 函数声明
- 表达式
- 函数的调用
fun.call(obj)
: 让一个函数成为一个陌生属性的方法是JS的一大特性AAA()
回调函数
-
什么是回调函数
- 自己定义的
- 自己没有调用(指的是没有写明,例如window.onclick就是一个没有调用的回调函数,是在特定时间自动执行的)
- 最后执行了
-
常见的回调函数
- dom事件回调函数(与用户交互比较重要的点,发生时间的DOM元素)
- 定时器回调函数
- ajax回调函数(与后台交互比较重要的点)
- 生命周期回调函数
IIFE
IIFE(Immediately-Invoked Function Expression) 立即调用函数表达式
一般来说我们写的是非匿名函数,这样可以将他存起来,当时我们也可以写没有对象接受内容的匿名函数,因为没有标识符标识他,所以他必须立即执行,否则以后就无法调用了(这里的非匿名不是说setInterval(function(){},1000)
这种,因为传值的时候相当于赋值了)
对于上述的匿名函数我们必须要当场执行
function(){
alert("123")'
}();
在后面加上([参数列表])
就可以执行了,因为我们将前面当作了一个整体去执行,最好前面也加上一个()
变成
(function(){
alert("123")'
})();
加上()
之后函数定义的变量就从之前的全局变量变成了局部变量,好处有:
- 隐藏实现:这样的话其他函数就看不到这个函数了(因为本身他就要匿名,最好不要让其他函数看见)
- 不会污染外部的命名空间
于是我们可以像C++中封装函数一样去封装一个JS函数
(function(){
function work1(){
// true work code 1;
}
function work2(){
// true work code 2;
}
function work2(){
// true work code 3;
}
window.XXX=function(){
if()...
return work1();
if()...
return work2();
return work3();
}
})();
我们希望实现一个模块,但是不想暴露细节函数,于是全员匿名,最后提供一个中控函数绑定到window,这在之后就是创建模块的方法,例如
var myModule = (function module(){
var someThing = "123";
var otherThing = [1,2,3];
function doSomeThing(){
console.log(someThing);
}
function doOtherThing(){
console.log(otherThing);
}
return {
doSomeThing:doSomeThing,
doOtherThing:doOtherThing
}
})();
myModule.doSomeThing();
myModule.doOtherThing();
函数中的this
注意的一点,对于如下的例子
function fun1(){
function fun2(){
console.log(this);
}
fun2();
}
fun1();
这里输出的this是window
,同时请注意以下代码
var tmp=new Object;
function fun1(){
console.log(this);
function fun2(){
console.log(this);
}
fun2();
}
tmp.fun=fun1;
tmp.fun();
返回
{fun: ƒ}
VM144:5 Window {0: Window, window: Window, self: Window, document: document, name: "", location: Location, …}
相当于只要不是明确是对象调的都是window
- this是什么
- 任何函数本质上都是通过对象调用的
- 所有函数内部都有一个变量this
- 他指向的是调用函数的当前对象
JS的分号问题
- 可加可不加
- 但是如果不加分号导致二义性要加(小中括号开头的),例如
会被认为是要执行一个名字叫做var a=3 (function(){ ...})()
3
的函数,参数列表为fun…
那么就要加了var a=3(function(){ ...})()
- 注意: JS在代码发布的时候会有一个
合理压缩
的过程,例如:
会被压缩成var a=123; function my_first_function(){ // ohh here is a note return 3; }
不仅压行,还直接改函数名…var a=123;function b(){ return 3;}
prototype
任何函数
都具有prototype属性,一个函数默认指向了一个Object空对象(原型对象),也就是说在函数被创建的时候会JS会默认创建一个对象,这个对象的内容是空的
原型对象的元素是给事例对象用的,
在prototype
中还有一个元素是constructor
这是一个引用变量,指向了指向prototype的对象,也就是构造函数与原型对象有一个相互引用的关系
显式原型与隐式原型
- 每个函数function都有一个
prototype
,也就是显式原型 - 每个事例对象都有一个
__proto__
,可以称为隐式原型 - 事例对象的
__proto__
的值是构造函数的prototype
- 注意:显式原型与隐式原型都是引用对象,指向的是原型,验证方法就是在创建元素后修改显式原型的内容
- 在ES6之前程序员可以直接操作显式原型,但是不能操作隐式原型
与其他语言不同的是,大部分语言采用的是基于类的基础而JS是采用基于对象的继承,这就导致JS存在原型链
原型链
我们可以尝试一直输出一个对象的__porto__
function Fn(){
var FnFunctionVals=1;
return 0;
}
return 666;
}
var fn=new Fn();
console.log("Fn IS ",fn);
console.log("Fn PROTO",fn.__proto__);
console.log("Fn PROTO PROTO",fn.__proto__.__proto__);
console.log("Fn PROTO PROTO PROTO",fn.__proto__.__proto__.__proto__);
console.log("Fn IS ",Fn);
console.log("Fn PROTO",Fn.__proto__);
console.log("Fn PROTO PROTO",Fn.__proto__.__proto__);
console.log("Fn PROTO PROTO PROTO",Fn.__proto__.__proto__.__proto__);
结果
Fn IS Fn__proto__: Object
Fn PROTO Objectconstructor: ƒ Fn()__proto__: Object
Fn PROTO PROTO Objectconstructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()arguments: (...)caller: (...)length: 1name: "isPrototypeOf"__proto__: ƒ ()[[Scopes]]: Scopes[0]propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()
Fn PROTO PROTO PROTO null
Fn IS ƒ Fn(){
var FnFunctionVals=1;
function tmp(){
return 666;
}
Fn PROTO ƒ () {
[native code] }
Fn PROTO PROTO Objectconstructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__