JS高级
数据类型
分类
基本(值)类型
String:任意字符串
Number:任意的数字
boolean :true/false
undefined :undefined
null :null
引用(对象)类型
Object :任意对象
Function :一种特别的对象 (可以执行)
Array : 一种特殊的对象(数值下标进行操作,内部数据是有序的)
判断
typeof: 可以判断undefined 数值 字符串 布尔值 function
不能区别判断null与object object与array
instanceof(实例) : 判断对象的具体类型
=== : 可以判断undefined null
//typeof返回数据类型的字符串表达
//1、基本
var a
console.log(a,typeof a,typeof a === 'undefined',a === undefined)
// 打印输出 undefined 'undefined' true true 如果我想知道a是不是undefined类型 就必须写 typeof a === 'undefined'
console.log(undefined === 'undefined') //打印输出false,目的是一定要辨别清楚undefined 'undefined'两者的区别
var a = 4
console.log(typeof a==='number')
a = 'jinzhuan'
console.log(typeof a=== 'string') //注意是小写的s
a = true
console.log(typeof a=== 'boolean')
a = null
console.log(typeof a) //返回object 所以不能用typeof来进行判断
//2、对象
var b1 = {
b2: [1,'abc',console.log],
b3: function () {
console.log('b3')
}
}
console.log(b1 instanceof Object,ba instanceof Object) //true false
console.log(b1.b2 instanceof Array,b1.b2 instanceof Object) //true true
console.log(b1.b3 instanceof Function,b1.b3 instanceof Object) //true true
console.log(typeof b1.b3==='function') // true
console.log(typeof bi.b2[2] === 'function') //true
console.log(b1.b3()())
//表达式() 这种形式,一般推断为函数类型
相关问题
1、实例对象与类型对象
//实例: 实例对象
//类型: 类型对象
function Person (name,age) { //构造函数 类型 但是别忘了函数也是一种特殊的对象
this.name = name;
this.age = age;
}
var p = new Person() //根据类型创建的实例对象
2、undefined与null的区别
undefined:
undefined代表定义未赋值
null:定义并赋值了,只是值为null
3、什么时候给变量赋值为null?
var b = null; //初始赋值为null,表明将要赋值为对象;
b = ['ljs',12]; //确定对象就赋值
b = null //让b指向的对象成为垃圾对象
1、初始赋值,表明将要赋值为对象
2、结束前,让对象成为垃圾对象(被垃圾回收器回收)
4、严格区别变量类型与数据类型?
数据类型
基本类型
对象类型
变量类型(变量值得类型)
基本类型:保存就是基本类型的数据
引用类型:保存的是地址值
数据_ 变量 _内存
什么是数据
存储在内存中代表特定信息的东西,本质是’0101···’
数据的特点?
可传递 可运算
一切皆数据
内存中所有操作的目标:数据
算术运算 逻辑运算 赋值运算 运行函数
什么是内存
内存条通电以后产生的可存储数据的空间(临时的)
内存产生和死亡:
内存条(电路板)——通电——产生内存空间——存储数据——处理数据——断电——内存空间和数据都消失
一块小内存的两个数据:
内部存储的数据
地址值数据
内存的分类
栈:全局变量 和 局部变量
堆:对象
什么是变量
可变化的量,由变量名,变量值组成
每个变量都对应的一块小内存,变量名用来查找对应的内存,变量值就是内存中保存的数据
内存、数据、变量之间的关系
内存用来存储数据的空间
变量时内存的标识
相关问题
关于内存与赋值的问题
问题:var a = xxx,a内存中到底保存的是什么
xxx是基本数据,保存的就是这个数据
xxx是对象,保存的就是对象的地址值
xxx是一个变量,保存的xxx的内存内容(可能是基本数据,也可能是地址值)
关于引用变量赋值问题
2个引用变量指向同一个对象,通过其中一个变量修改对象内部数据,另一个变量看到的是修改之后的数据
在js调用函数时传递变量参数时,是值传递还是引用传递
理解:都是值(基本值/地址)传递
var a = 3;
function fn(a) {
a = a+1;
}
fn(a);
console.log(a);
//输出3,函数传递时传的是3这个值!!!搞清楚两个a不是同一个
function fn2 (obj) {
console.log(obj.name)
}
var obj = {name:'Tom'}
//两个obj之间传递的是地址的值!!!
JS引擎如何管理内存?
1、内存生命周期
分配内存空间,得到它的使用权
存储数据,可以反复进行操作
释放空间小内存空间
var a = 3
var obj = {} //到这一步,有三个内存空间
obj = null //到这一步,有两个内存空间
function fn() {
var b = {}
}
fn() //b是自动释放,b所指向的对象是在后面的某个时刻由垃圾回收器回收
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXZ240X7-1664326154876)(JS高级.assets/1662904830107.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E8VTsM3T-1664326154877)(JS高级.assets/1662905280254.png)]
JS对象
什么是对象
多个数据的封装体
用来保存多个数据的容器
一个对象代表现实中的一个事物
为什么要用对象
统一管理多个数据
对象的组成
属性:属性名(字符串)和属性值(任意)组成
方法:一种特别的属性(属性值是函数)
如何访问对象内部数据
.属性名 :编码简单,有时不能用
[‘属性名’]:编码麻烦,能通用
什么时候必须使用[‘属性名’]
1、属性名包含特殊字符
2、变量名不确定
函数
什么是函数
实现特定功能的n条语句的封装体
只有函数是可以执行的,其他类型的数据不能执行
为什么要函数
提高代码复用
便于阅读交流
如何定义函数
函数声明
表达式
如何调用(执行)函数
test():直接调用
obj.test() :通过对象调用
new test() :new调用
test.call / apply(obj): 临时让test称为obj的方法进行调用
回调函数
什么函数才是回调函数
你定义的
你没有调
但最终它执行了
常见的回调函数
dom事件回调函数
定时器回调函数
ajax请求回调函数
生命周期回调函数
IIFE匿名函数自调用(立即执行函数)
1、理解
全称:Immediately-Invoked Function Expression
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SNA8CTME-1664326154878)(JS高级.assets/1663203333708.png)]
2、作用
隐藏实现
不会污染外部命名空间
(function() {
var a = 1;
function test() {
console.log(++a)
}
$ = function() {
return {
test : test
}
}
})()
$().test(); $是一个函数,执行后返回一个对象,这个对象含有test方法
3、this指向
在IIFE中,this指向window
var num = 10
var obj = {num: 20}
obj.fn = (function (num) {
console.log(num)
this.num = num * 3
num++
console.log(num)
return function (n) {
this.num += n
num++
console.log(num)
}
})(obj.num)
var fn = obj.fn
fn(5)
obj.fn(10)
console.log(num,obj.num);
//输出:22 23 65 30
首先呢,立即执行函数一定是在代码执行的时候就已经开始执行了,而最后赋值的对象就是立即执行函数里面的return返回的东西。此外,一定要明白立即执行函数里面的参数没有被销毁而是保存在了赋值对象上面,也就是说值还在,搞不懂程序的时候就要多调试。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CvdBGHI6-1664326154879)(JS高级.assets/885ef95423d7000c30ef052006772c1.jpg)]
函数中的this
this是什么
任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window
所有函数内部都有一个变量this
它的值是调用函数的当前对象
如何确定this的值
test():window
p.test():p
new test():新创建的对象
p.call(obj):obj
JS语句中分号问题
1、js一条语句的后面可以不加分号
2、是否加分号是编码风格问题,没有应该不应该,只有你自己喜欢不喜欢
3、在下面2中情况下不加分号会有问题
小括号开头的前一条语句
中括号开头的前一条语句
4、解决方法:在行首加分号
5、强有力的例子:vue.js库
函数的prototype属性
函数的prototype属性
1、每个函数都有一个prototype属性,它默认指向一个一个Object空对象,就是原型对象
原型对象中有一个属性constructor,它指向函数对象
2、给原型对象添加属性(一般都是方法)
作用:函数的所有实例对象自动拥有原型中的属性(方法)
显示原型与隐式原型
1、每个函数function都有一个prototype,即显示原型
2、每一个实例对象都有一个_ proto _,可称为隐式原型
3、对象的隐式原型的值为其对应构造函数的显示圆形的的值
4、
5、总结:
函数的prototype属性:在定义函数时自动添加的,默认值是一个空Object对象
对象的_ proto _属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
执行上下文和上下文栈
变量声明提升
通过var定义(声明)的变量,在定义语句之前就可以访问到
值:undefined
函数声明提升
通过function声明的函数,在之前就可以直接调用
值:函数定义(对象)
问题
变量提升和函数提升是如何产生的
执行上下文
1、代码分类
全局代码
函数(局部)代码
2、全局执行上下文
在执行全局代码前将window确定为全局执行上下文
对全局数据进行预处理
var定义的全局变量==>undefined,添加为window的属性
function声明的全局函数==>赋值(fun),添加为window的方法
this==>赋值(window)
开始执行全局代码
3、函数执行上下文
在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象
对局部数据进行预处理
形参变量==> 赋值(实参) ==> 添加为执行上下文的属性
arguments==> 赋值(实参列表),添加为执行上下文的属性
var定义的局部变量==>undefined,添加为执行
执行上下文栈
1、在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象
2、在全局执行上下文(window)确定后,将其添加到栈中(压栈)
3、在函数执行上下文创建后,将其添加到栈中(压栈)
4、在当前函数执行完后,将栈顶的对象移除(出栈)
5、当所有的代码执行完后,栈中只剩下window
console.log('global begin:' + i)
var i = 1
foo(1);
function foo(i) {
if(i == 4){
return ;
}
console.log('foo() begin:' + i);
foo(i + 1);
console.log('foo() end:' + i);
}
console.log('global end:' + i)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VpUTXKPv-1664326154879)(JS高级.assets/1663250350631.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YkhqsK42-1664326154880)(JS高级.assets/1663250473576.png)]
js中关于作用域(var定义的变量)的解析:
var定义的变量(函数内外部)的解析
var a = 2;//全局变量
function func(){
var b = 1;//局部变量
c = 3;//全局变量
}
函数外面定义的变量是全局变量,函数可以直接使用
在函数内部没有使用var定义的变量则为全局变量
在函数内部使用var关键字定义的变量是局部变量,即出了函数外边无法获取这个变量
显式声明与隐式声明
显式声明
使用var关键字 + 变量名在(标识符)的方式在function外部声明,即为全局变量。否则在函数内部声明的变量即为局部变量。该方式为显式声明
var test = 5;//全局变量
function b(){
var a = 3;//局部变量
}
隐式声明
没有使用var,直接在函数内给test赋值,这样会隐式声明了全局变量test
test = 5;//全局变量
function a(){
d = 3;//d也是全局变量
}
var a = 5;
function fn() {
var a = '哈哈';
console.log(a);//哈哈 函数fn内的a
}
fn();
console.log(a);//5 外部的a
var a = 5;
function fn() {
a = '哈哈';
console.log(a);//哈哈
}
fn();
console.log(a);//哈哈
/*调用函数fn(); 由于在函数内部没有定义a,a = '哈哈';是外部的a,
把var a = 5;中的a覆盖了,函数中打印的a是外部被覆盖掉的a,
函数外打印的a还是外部被覆盖掉的a*/
function fn() {
console.log(a);//undefined
var a = '哈哈';
console.log(a);//哈哈
}
fn();
/*调用函数fn(); 函数中打印的第一个a是函数内部的,变量提升到函数作用域顶端的a,
声明了但未赋值,函数中打印的第二个a是赋值为哈哈后的a*/
var a = 5;
function fn(a) {
a = '哈哈';
console.log(a);//哈哈
}
fn(0);
console.log(a);//5
/*调用函数fn(0); 把实参0传递给形参a,此时相当于在函数内部声明a并赋值为0,
var a = 0,函数内部的a = '哈哈';会把a=0覆盖掉,所以内部打印的是哈哈,
外部打印的还是外部的a,是5*/
var a = 5;
function fn() {
console.log(a);//5
a = '哈哈';
}
fn('嘿嘿');
console.log(a);//哈哈
/*调用函数fn('嘿嘿'); 实参嘿嘿传不到函数中,因为函数中没有形参,函数中打印的a
依然是外部的a是5,接着a = '哈哈';把函数外部的a=5覆盖了,所以函数外部打印的a是
哈哈*/
var a = 5;
function fn(a) {
console.log(a);//5
a = '哈哈';
}
fn(a);
console.log(a);//5
/*调用函数fn(a); 由于外部声明赋值了var a = 5;,所以把实参5传给了函数的形参a,
相当于在函数内部声明并赋值了var a = 5;然后在函数内部打印a
是5,然后函数内部的a = '哈哈';把函数内部的var a = 5;中的a覆盖了。
在函数外部打印的a依然是外部的a,所以是5*/
作用域与作用域链
1、理解
就是一块地盘,一个代码段所在的区域
它是静态的,相当于上下文对象,在编写代码时就确定了
2、分类
全局作用域
函数作用域
没有块作用域(ES6)
3、隔离变量,不同作用域下同名变量不会有冲突
var x = 10;
function fn() {
console.log(x); //输出10;
}
function show (f){
var x = 20;
f();
}
show(fn); //将fn作为一个函数变量参数传进去
var fn = function() {
console.log(fn);
}
fn() //输出这个函数
var obj = {
fn2: function() {
console.log(fn2)
}
}
obj.fn2() //会报错,因为在function这个函数局部作用域中,找不到fn2,这个时候就会去全局作用域去找,也没有找到,那么就会有人有问题,这个为什么不找obj里面的fn2,如果要找obj里面的fn2,那么就要改写成 this.fn2
闭包
外部函数被创建几次,闭包就生成几个,跟内部函数执行几次没有关系,在反复执行闭包的过程中,闭包中的数据没有消失
常见的闭包
1、将函数作为另一个函数的返回值
2、将函数作为实参传递给另一个函数调用
闭包的作用
1、使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
2、让函数外部可以操作(读写)到函数内部的数据(变量/函数)
闭包的生命周期
1、产生:在嵌套内部函数定义执行完时就产生了(不是在调用),有函数提升!!!
2、死亡:在嵌套的内部函数成为垃圾对象时
function fn1() {
//此时闭包就已经产生了(函数提升,内部函数对象已经创建了)
var a = 2;
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f()
f = null //闭包死亡,包含闭包的函数对象成为垃圾对象,f是包含闭包函数的,当赋值null后,就变成了垃圾对象
闭包的应用:定义JS模块
具有特定功能的js文件
将所有的数据和功能都封装在一个函数内部(私有的)
只向外暴露一个包信n个方法的对象或函数
模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
内存溢出与内存泄漏
闭包的缺点及解决
缺点:
1、函数执行完后,函数内的局部变量没有释放,占用内存的时间会边长
2、容易造成内存泄露
解决:
1、能不用闭包就不用
2、及时释放
内存溢出
一种程序运行的错误
当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误
内存泄漏
占用的内存没有及时释放
内存泄漏积累多了就容易导致内存溢出
常见的内存泄漏:
意外的全局变量
没有及时清理的计时器或回调函数
var name = 'The window'
var object = {
name : 'My Object',
getNameFunc : function() {
return function() {
return this.name;
};
}
};
alert(object.getNameFunc()())
//弹窗会弹出The window,因为返回的函数中的this指向的是window
var name = 'The window'
var object = {
name : 'My Object',
getNameFunc : function() {
that = this
return function() {
return that.name;
};
}
};
alert(object.getNameFunc()())
//弹窗会弹出My Object,因为返回的函数中的this指向的是object
原型链继承
套路
1.定义父类型构造函数
2、给父类型的原型添加方法
3、定义子类型的构造函数
4、创建父类型的对象赋值给子类型的原型
5、将子类型原型的构造属性设置成子类型
6、给子类型原型添加方法
7、创建子类型的对象:可以调用父类型的方法
关键
子类型的原型为父类型的一个实例对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-354Vtz6q-1664326154880)(JS高级.assets/1663589709661.png)]
function Supper() {
this.supProp = 'Supper property'
}
Supper.prototype.showSupperProp = function() {
console.log(this.supProp)
}
//子类型
function Sub() {
this.subProp = 'Sub property'
}
//子类型的原型为父类型的一个实例对象
Sub.prototype = new Supper()
Sub.prototype.showSubProp = function() {
console.log(this.subProp)
}
var sub = new Sub()
sub.showSupperProp()
组合继承
原型链+借用构造函数的组合继承
1、利用原型链实现对父类型对象的继承
2、利用supper ()借用父类型构造函数初始化相同属性
function Supper() {
this.supProp = 'Supper property'
}
Supper.prototype.showSupperProp = function() {
console.log(this.supProp)
}
//子类型
function Sub() {
this.subProp = 'Sub property'
}
//子类型的原型为父类型的一个实例对象
Sub.prototype = new Supper()
Sub.prototype.showSubProp = function() {
console.log(this.subProp)
}
var sub = new Sub()
sub.showSupperProp()
console.log(sub)
//会遇到一个问题就是此时sub的constructor指向的是Supper,本应该指向Sub
function Supper() {
this.supProp = 'Supper property'
}
Supper.prototype.showSupperProp = function() {
console.log(this.supProp)
}
//子类型
function Sub() {
this.subProp = 'Sub property'
}
//子类型的原型为父类型的一个实例对象
Sub.prototype = new Supper()
Sub.prototype.constructor = Sub //重点!!!
Sub.prototype.showSubProp = function() {
console.log(this.subProp)
}
var sub = new Sub()
sub.showSupperProp()
console.log(sub)
function fun(n,o) {
console.log(o)
return {
fun:function(m) {
return fun(m,n)
}
}
}
var a = fun(0)
a.fun(1)
a.fun(2)
a.fun(3) //undefined,0,0,0,产生了新的闭包,但是立刻就消失了,如果写成xxx = a.fun(1)就可以承接,但是没有人承接,所以刚产生的闭包立即就消失了
var b = fun(0).fun(1).fun(2).fun(3) //undefined,0,1,2
var c = fun(0).fun(1)
c.fun(2)
c.fun(3) //undefined,0,1,1
//重点是一点要注意产没有产生新的闭包
借用构造函数继承(假的)
1、套路
定义父类型构造函数
定义子类型构造函数
在子类型构造函数中调用父类型构造
2、关键:
在子类型的构造函数中通过call()调用父类型的构造函数
进程与线程
进程
程序的一次执行,它占有一片独有的内存空间,可以通过windows任务管理器查看进程
线程
是进程内的一个独立执行单元
是程序执行的一个完整的流程
是CPU的最小的调度单元
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RDqDcFIi-1664326154881)(JS高级.assets/1663841084807.png)]
应用程序必须运行在某个进程的某个线程上
一个进程中至少有一个运行的主线程:主线程,进程启动后自动创建
一个进程中也可以同时运行多个线程,我们会说程序是多线程运行的
一个进程内的数据可以供其中的多个线程直接共享
多个进程之间的数据是不能直接共享的
线程池:保存多个线程对象的容器,实现线程对象的反复利用
多进程运行:一应用程序可以同时启动多个实例运行
多线程:在一个进程内,同时有多个线程运行,优点:能有效提升CPU的利用率,创建多线程开销 缺点:线程间切换开销,死锁与状态同步问题
单线程,优点:顺序编程简单易懂,缺点:效率低
JS是单线程还是多线程?
JS是单线程运行的
但使用H5中的Web Workers可以多线程运行
浏览器运行是单线程还是多线程?
都是多线程运行的
浏览器运行时单进程还是多进程?
有的是单进程
有的是多进程
定时器引发的思考
定时器是定时执行的吗?
定时器并不能保证真正定时执行
一般会延迟一丁点(可以接受),也有可能延迟很长时间(不能接受)
定时器回调函数是在分线程执行吗?
在主线程执行,js是单线程的
定时器是如何实现的?
时间循环模型
js引擎执行代码的基本流程
先执行初始化代码,包含一些特别的代码
设置定时器
绑定监听
发送ajax请求
后面在某个时刻才会执行回调代码
一点要注意产没有产生新的闭包
### 借用构造函数继承(假的)
1、套路
定义父类型构造函数
定义子类型构造函数
在子类型构造函数中调用父类型构造
2、关键:
在子类型的构造函数中通过call()调用父类型的构造函数
### 进程与线程
#### 进程
程序的一次执行,它占有一片独有的内存空间,可以通过windows任务管理器查看进程
#### 线程
是进程内的一个独立执行单元
是程序执行的一个完整的流程
是CPU的最小的调度单元
[外链图片转存中...(img-RDqDcFIi-1664326154881)]
应用程序必须运行在某个进程的某个线程上
一个进程中至少有一个运行的主线程:主线程,进程启动后自动创建
一个进程中也可以同时运行多个线程,我们会说程序是多线程运行的
一个进程内的数据可以供其中的多个线程直接共享
多个进程之间的数据是不能直接共享的
线程池:保存多个线程对象的容器,实现线程对象的反复利用
多进程运行:一应用程序可以同时启动多个实例运行
多线程:在一个进程内,同时有多个线程运行,优点:能有效提升CPU的利用率,创建多线程开销 缺点:线程间切换开销,死锁与状态同步问题
单线程,优点:顺序编程简单易懂,缺点:效率低
### JS是单线程还是多线程?
JS是单线程运行的
但使用H5中的Web Workers可以多线程运行
### 浏览器运行是单线程还是多线程?
都是多线程运行的
### 浏览器运行时单进程还是多进程?
有的是单进程
有的是多进程
### 定时器引发的思考
#### 定时器是定时执行的吗?
定时器并不能保证真正定时执行
一般会延迟一丁点(可以接受),也有可能延迟很长时间(不能接受)
#### 定时器回调函数是在分线程执行吗?
在主线程执行,js是单线程的
#### 定时器是如何实现的?
时间循环模型
### js引擎执行代码的基本流程
先执行初始化代码,包含一些特别的代码
设置定时器
绑定监听
发送ajax请求
后面在某个时刻才会执行回调代码