JAVAScript (基础一)
- 浏览器如何运行JS代码:
浏览器分成两部分:渲染引擎和JS引擎
渲染引擎:用来解析HTML和CSS,又叫内核不如Chrome(blink), 老版本是(webkit)
JS引擎:称之为JS解释器,用来读取网页中JavaScript的代码,其处理后运行,比如Chrome的V8,JS引擎将语言转换成二进制语言
JavaScript: EcmAScript, DOM ,BOM 基础知识;
DOM: 文档对象模型,对页面上各种元素进行操作;
BOM: 浏览器对象模型,对浏览器的操作
Js 语言书写位置分为 行内,内嵌和外部方式,一般采用外部方式,只需要在HTML写入使用浏览器打开就行,不像TS还需要进行编译成JS代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name = "viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 内嵌头头部-->
<script>
alert('沙漠骆驼');
</script>
<!--外部引入 注意没有写路径化,js文件放在与HTML同级别目录下-->
<script src="***.js"></script>
</head>
<body>
<!-- 行内式方式-->
<input type="button" value ="button" onclick="alert('button onclick')">
</body>
</html>
2. 函数和回调函数:
JS中函数也是个对象,通过使用回调函数,由于JS是单线程方式,采用异步调用方式,进行返回
JS中可以让 任意函数成为任意对象的方法进行调用: test2.cal(obj); test2是一个函数,obj是一个对象
- 匿名函数自调用:
功能:局部变量,不会污染全局变量
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<!--
IIFE:
1. nampace
-->
<body>
<script type ="text/javascript">
// if you cannot use ;,you can use ; in the head of function : ;(function()=>{})()
(function () {
// 匿名函数自己调用自己 IIFE
var a = 1;
function test(){
console.log(++a);
};
// export the inner function
window.$ = function () {
return {test:test};
};
})();
// $ is function name ,2. $ return is a object
$().test(); // the output is 2
</script>
</body>
</html>
- 对于this 理解: window对象时浏览器给每一个HTML创建的对象,所以THIS指定有不同对象
this: 任何函数本质上通过某个对象调用(显示还是隐式调用,没有指定是由Window对象调用),值是调用函数的当前对象
test() // widnow
p.test()// p
new test():新创建对象
- 关于分号问题
1.小括号开头的前一条语句需要添加分号: 匿名函数自调用
2.函数定义需要添加分号
3.中括号开头的前一条语句;
;[1, 2].forEach(function(){
})
3.函数高级: 1,原型和原型链 2. 执行上下文与执行上下文栈 3.作用域和
- prototype属性:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<!--
函数的prototype 图:
1.每一个函数有prototype 属性,默认指向object 空对象
2. 原型对象有constructor , 指向函数对象 构造函数与原型对象相互引用
-->
<body>
<script type="text/javascript">
console.log(Date.prototype, typeof Date.prototype);
function Fun(){
}
console.log(fun.prototype) //默认指向object空对象,继承父Obejct对象
// js 动态添加属性
Fun.prototype.test = function(){
console.log("tets:()")
}
// 使用:prototype 添加属性(一般是方法)给实例对象可以访问
var fun = new Fun()
fun.test()
</script>
</body>
</html>
- 显式原型和隐式原型: test()函数,查找:通过隐式原型对象查找,但是隐式原型值由显式原型值赋值
- 原型链:<!--
原型链: 访问对象属性查找流程:
1.先在自身属性中查找,找打返回
2. 如果没有,沿着__proto__ 链进行查询
3. Object.__proto__ null 没有返回undefined
本质是 隐式原型链
作用: 查找对象属性/方法,作用域链查找对象是否存在
-->任何函数 本质上 new Fucntion()实例对象(类比Class类),有两个属性显式prototype,隐式__proto__只有实例对象采用隐式属性
实例对象的隐式原型(__proto__)等于其构造函数显式原型Fuction.prototype
所有函数的__proto__都是一样通过new Function()产生等同于Fucntion.prototype
原型链可以继承,类似于JAVA中继承
- 查看两个特殊函数 Object,Function 函数
- 1. 函数的显式原型(prototype)指向对象默认的空Object对象,但是Object.prototype为null
2. 所有函数都是 Fuction() 的实例,包括本身,funtion foo(){}: foo 是Function的实例
3. Object 原型对象时原型链的尽头:
4. 原型对象的属性:设置属性不会查找原型链,如果当前对象没有此属性,直接添加此属性,查找属性才会通过原型方式
一般不会在prototype 上添加属性,一般通过构造方法直接在对象上添加属性
注意 *********:实例函数__proto__等于构造函数的显式原型prototype (类的实例对象的隐式原型__proto__ 等于Class 的prototype)
A instance of B : (A是否是类B的实例)
4. 函数高级:
- 4.1:变量提升和函数提升
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--输出 undefined
在定义之前输出 访问到,但只是属于undefined
通过 var 定义变量在定义之前可以访问,但是值属于undefined
函数声明提升:
通过function声明含函数,在之前可以直接调用,值是函数定义对象
3.问题: 如何产生的
-->
<script type="text/javascript">
var a = 3
function fn() {
console.log(a)
var a = 4
}
fn2() // 属于函数提升
fn3() // 不能调用,定义属于一个变量,属于变量提升
function fn2() {
console.log("fn2")
}
var fn3 = function fn3() {
console.log("fn3")
}
</script>
</body>
</html>
- 4.2 函数上下文切换
-
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!--输出 undefined 1.全局执行上下文: 执行全局代码前将window确定为全局执行上下文 对全局数据进行预处理 var 定义全局变量为window属性 ==> undefined function 声明全局函数 赋值为window方法,调用方法提前执行 this ==》 赋值为widow 2.调用函数上下文,创建一个函数执行上下文对象 对局部数据进行预处理: 1. --> <script type="text/javascript"> console.log(a1, widnow.a1) a2() // 调试时候发现下面test2 函数不会执行,在这里已经执行过去了 console.log(this) var a1 = 3 function a2() { console.log("test2") } // 函数执行上下文 function fn(a1) { console.log(a1) // 2 console.log(a) // undefined a3() // a3 console.log(this) //window console.log(arguments) // 2,3 伪数组 var a = 3 function a3() { console.log("a3") } console.log("fn") } fn(2,3) </script> </body> </html>
-
5. 闭包:
-
理解: 有权访问另外一个函数作用域中变量函数,创建闭包的常见形式,在一个函数内部创建另外一个函数,通过另外一个函数访问这个函数的局部变量
5.1 作用域和作用与链:知道理解对于变量性函数是在循环结束之后才会运行
1. Java有块作用域的,但是JS没有块作用域在ES6之前不存在
循环变量加闭包实现 不断监听: 普通访问方式中匿名函数无法在循环中获取其循环中变量值,所以不得不适用闭包
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
// 对于伪数组 不断重复的计算length
var btns = document.getElementsByTagName("button")
var length = btns.length // btns 伪数组,.length 需要不断调用方法计算
// 遍历增加监听函数, i 与 Java有点区别 i 为全局变量
for (var i = 0; i < length; i++) {
var btn = btns[i]
// 函数在循环结束后才执行, 匿名函数赋值给 变量,明显这种方式不太行,匿名函数问题
btn.onclick = function () {
alert("第" + (i + 1) + " button")
}
}
// 正确写法:1
for (var i = 0; i < length; i++) {
var btn = btns[i]
btn.index = i
// 函数在循环结束后才执行, 匿名函数赋值给 变量,明显这种方式不太行,匿名函数问题
btn.onclick = function () {
alert("第" + (this.index + 1) + " button")
}
}
// 正确写法:
for (var i = 0; i < length; i++) {
// n匿名哈数自调用
(function (i) {
var btn = btns[i] // i 与外部的 i 不一样
btn.onclick = function () {
alert("第" + (this.index + 1) + " button")
}
})(i)
}
</script>
</body>
</html>
常见的闭包函数:
对于JavaScript中闭包理解:
1. 首先理解JS中变量作用域 : 全局变量 和局部变量, 匿名函数之内读取全局变量同理函数外部无法读取函数内部局部变量,因此出现一个问题,我们如何在外部读取局部变量(业务要求)?
问题解决途径: 1). 在函数内部定义一个变量: 在链式作用域条件下,子函数会一级级向上寻找所有父亲对象,fn2 为 fn1 的内部函数, 既然Fn2 函数可以读取Fn1局部变量,因此需要将F2作为返回值,在f1 外部读取器内部变量
总结L闭包出现:为了让能够读取其他函数内部变量的函数,将函数内部和外部链接起来的一座桥梁
如下图所示: result 实际上闭包函数f2,一共运行2次,证明函数f1 中局部变量n 一直保存在内存当中没在f1调用之后被清除
多次引用时候注意闭包情况下会修改内部变量值
注意一点: 闭包会将函数中变量被保存在内存当中,内存消耗很大,所以不能使用闭包,造成网页性能问题。闭包会在父函数外部,修改时候福函数内部的值,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
对于闭包中:函数的内外变量进行交互形成闭包;
- 闭包内存溢出和内存泄漏:
过多的使用闭包,函数执行完成之后,内部局部变量没有释放掉,容易产生内存堆积,容易造成内存溢出
1. 理解全局变量和局部变量概念:
1). 使用关键字var 定义的变量 在function 外是全局变量,在function内部是局部变量,如果没有var 关键字标识,无论在函数里外都是全局变量
2). js 在执行前对整个脚本文件中 变量声明做一个完整的分析,从而确定变量的作用域,当局部变量与全局变量重名,局部变量会覆盖全局变量,当局部变量离开后,重新回到全局变量,如果想在局部变量过程中使用全局变量: window.globalVaraiableName
3) . 常见的内存泄漏问题:
a. 不小心在函数内部定义全局变量(隐式方式定义)
b. 使用setInterval()定时器,忘记使用ClearInterval 将申请的定时器进行删除
c . 闭包使用,使用完成之后必须将其置空,防止内存泄漏
如何保证子类型的实例指向本身:
子类型的prototype的construct指向子类型
Sub.prototype.construct = Sub
call 关键字:能够让我们使用其他对象的方法,无需本对象对现有方法重复书写
- 组合继承: 通过这种组合继承方法获取父函数的特性和其中一些方法
// 假设:
function Person (age,name){
this.age = age
this.name = name
}
Person.prototype.setName = function(name){
this.name = name
}
function Student(age, name, socre){
Person.call(this, age, name)// 为了让 子类 获取属性
this.score = score
}
// 获取父类的方法 获取父类方法
Student.prototype = new Person()
// 修正construct 属性
Student.prototype.constructor = Student
var s = new Student(12, 'Tom', 34)
- 6. JS 是单线程还是多线程:
JS是单线程, 在HTML5中使用WEB WORK方式解决单线程问题
WEB WORK方式, 浏览器是多线程运行的,Chrom使用多进程方式,老版本的IE的单进程方式;
// JS 单线程执行的 例子
<body>
<script type="text/javascript">
setTimeout(function (){
console.log(" timeout 2222")
alert("2222")
}, 2000)
setTimeout(function(){
console.log(" timeout 1111")
},2000)
function fn(){
console.log("fn")
}
fn()
console.log("alert 之前")
alert(" alter 一个框架") // 暂停当前主线程执行,同时也暂停计时,点击继续才会恢复
console.log("alert 值后")
</script>
<!--
1. 定时器引发的问题: 让定时器长时间工作, 定时器函数不能100% 保证完全执行
定时器中函数是在主线程中执行的,主要是JS是单线程执行方式 主要原因是事件循环模型 导致无法准确让定时器执行成功!!
2.代码分为: 初始化代码和回调代码
setTimeout 是初始化代码,里面function 属于回调代码
执行顺序: 先执行初始化 包含一些特别事件
后执行回调代码(异步执行):
1.设置定时器
2. 绑定事件监听
3. ajax请求
为什么JS设定成单线程??
多线程情况下,多个线程同时操作同一个标签,线程切换过程中会无法实现界面的效果
-->
虽然HTML5 中WEN WORK方式开启多线程,但是还是要确保有且只有一个线程操作当前页面。
stack中将回调函数压入堆栈中,发现定时器任务会先执行后面的,在执行前面的,定时器函数和绑定事件监听函数执行顺序就不一定按照什么顺序执行
event loop : 事件循环 当初始化代码执行后才会执行回调代码
- WEB WORKERS 多线程:
WEB WORKERS 多线程使用方式,WEB WORKER 规范两类工作现场,分别是专用线程 Dedicated Worker和 共享线程Shared Worker ,Dedicated Worker 只能为一个页面所使用,但是Shared Worker 可以被多个页面使用。
分线程中全局对象不再是WINDOW,在分线程中不能更新界面操作, WORKER中数据通信 主要通过onmessage 事件和postMessage()事件实现的,WOKER的主页与分页之间数据传递通过是拷贝,因此设计数据序列号和反序列化,数据拷贝,所以在一端进行数据修改不会影响另外一端,解决大量计算对UI渲染的阻塞问题
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------总结:
1. js中函数也是一个对象,确切说函数式用Function构造函数创建一个Function类的对象,Function对象包含一个字符串,字符串中包含函数的JS代码,因此在JS中将函数作为参数传递,向这种直接将函数或者匿名函数作为参数传递的方式叫做是回调函数
funtion say(value){
console.log(value)
}
console.log(say)
console.log(say('I lOVE YOU'))
// 只写变量名say 返回将会是say方法本身,以字符串形式表现lai
// 在变量名后加(),就是属于方法调用
2.异步调用: JS是属于同步方式,在同步过程中不可能进行request或者耗时较大的操作,所以我们这采用异步调用方式
使用关键字 async, await 进行级联调用,基于协程机制,进行中断,去处理其他事件,独立完成与界面无关工作,不阻塞主线程渲染界面,不是很卡,可以自由控制运行和阻塞状态