JavaScript
原型链
每创建一个函数,解析器都会想函数中添加一个prototype原型属性,这个prototype属性对应着一个对象,这个对象就是原型对象
如果该函数是普通函数调用,则prototype没作用
如果该函数是以构造函数调用,则创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,通过__prototype__来访问该属性
对象.hasOwnProperty(属性) //对象自身中是否包含某种属性
原型对象也是对象,所以也有原型 object对象没有原型
对象要使用某个属性或方法,会现在自身寻找→原型对象中寻找→原型对象的原型中寻找......直到object对象原型
数组
会影响原数组的数组方法:push,pop,shift,unshift,reverse,splice,sort
<script>
let arr = [1,2,3,4,5]
//返回截取从0到3组成的新数组
console.log(arr.slice(0,3));
//返回删除索引0开始的1个元素 并用22,33插入删除的位置
console.log(arr.splice(0,1,'22','33'));
</script>
数组去重
<script>
let arr = [1, 2, 2, 2, 3, 4, 5, 2, 3, 1, 2, 0]
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) {
arr.splice(j, 1)
j--
}
}
}
console.log(arr.sort())
</script>
this的情况
一、隐式绑定 没有 对象.函数 指向window
--函数调用时前面并未指定任何对象,这种情况下this指向全局对象window。
function fn1() {
let fn2 = function () {
console.log(this); //window
fn3();
};
console.log(this); //window
fn2();
};
function fn3() {
console.log(this); //window
};
fn1();
--如果函数调用时,前面存在调用它的对象,那么this就会隐式绑定到这个对象上
函数调用前存在多个对象,this指向距离调用自己最近的对象
function fn() {
console.log(this.name);
};
let obj = {
name: '行星飞行',
func: fn,
};
let obj1 = {
name: '听风是风',
o: obj
};
obj1.o.func() //行星飞行
二、 隐式丢失
在特定情况下会存在隐式绑定丢失的问题,最常见的就是作为参数传递以及变量赋值
var name = '行星飞行';
let obj = {
name: '听风是风',
fn: function () {
console.log(this.name);
}
};
function fn1(param) {
param();
};
fn1(obj.fn);//行星飞行
// obj.fn 也就是一个函数传递进 fn1 中执行,单纯传递了一个函数而已,this并没有跟函数绑在一起,所以this丢失这里指向了window。
var name = '行星飞行';
let obj = {
name: '听风是风',
fn: function () {
console.log(this.name);
}
};
let fn1 = obj.fn;
fn1(); //行星飞行
三、显式绑定(通过call、apply以及bind方法改变this的行为)
call与apply让函数从被动变主动,函数能主动选择自己的上下文,以此方法改变this指向时,指向参数提供的是null或者undefined,那么 this 将指向全局对象。
let obj1 = {
name: '听风是风'
};
let obj2 = {
name: '时间跳跃'
};
var name = '行星飞行';
function fn() {
console.log(this.name);
};
fn.call(obj1); //听风是风
fn.apply(obj2); //时间跳跃
fn.call(undefined); //行星飞行
fn.apply(null); //行星飞行
fn.bind(undefined)(); //行星飞行
call、apply与bind有什么区别?
1.call、apply与bind都用于改变this绑定,但call、apply在改变this指向的同时还会执行函数,而bind在改变this后是返回一个全新的boundFcuntion绑定函数,这也是为什么上方例子中bind后还加了一对括号 ()的原因。
2.bind属于硬绑定,返回的 boundFunction 的 this 指向无法再次通过bind、apply或 call 修改;call与apply的绑定只适用当前调用,调用完就没了,下次要用还得再次绑。
let obj1 = {
name: '听风是风'
};
let obj2 = {
name: '时间跳跃'
};
var name = '行星飞行';
function fn() {
console.log(this.name);
};
fn.call(obj1); //听风是风
fn(); //行星飞行
fn.apply(obj2); //时间跳跃
fn(); //行星飞行
let boundFn = fn.bind(obj1);//听风是风
boundFn.call(obj2);//听风是风
boundFn.apply(obj2);//听风是风
boundFn.bind(obj2)();//听风是风
3.call与apply功能完全相同,唯一不同的是call方法传递函数调用形参是以散列形式,而apply方法的形参是一个数组。在传参的情况下,call的性能要高于apply,因为apply在执行时还要多一步解析数组。
let obj = {
name: '听风是风'
};
function fn(age,describe) {
console.log(`我是${this.name},我的年龄是${age},我非常${describe}!`);
};
fn.call(obj,'26','帅');//我是听风是风,我的年龄是26,我非常帅
fn.apply(obj,['26','帅']);//我是听风是风,我的年龄是26,我非常帅
四、new绑定
function Fn(){
this.name = '听风是风';
};
let echo = new Fn();
echo.name//听风是风
五、this的绑定优先级
显式绑定 > 隐式绑定 > 默认绑定
new绑定 > 隐式绑定 > 默认绑定
六、箭头函数的this
箭头函数中没有this,箭头函数的this指向取决于外层作用域中的this,外层作用域或函数的this指向谁,箭头函数中的this便指向谁。一旦箭头函数的this绑定成功,也无法被再次修改,可以通过修改箭头函数的外层作用域达到修改箭头函数的this。
function fn() {
return () => {
console.log(this.name);
};
};
let obj1 = {
name: '听风是风'
};
let obj2 = {
name: '时间跳跃'
};
fn()
fn.call(obj1)(); // fn this指向obj1,箭头函数this也指向obj1
fn.call(obj2)(); //fn this 指向obj2,箭头函数this也指向obj2
Math
Math.ceil(浮点数) 小数向上取整
Math.floor(浮点数) 小数向下取整
Math.round(浮点数) 小数四舍五入
Math.max(数字,数字,数字) 取数字们中最小的
Math.max(数字,数字,数字) 取数字们中最大的
生成一个x,y之间的随机数
Math.round(Math.random()*(y-x)+x)
样式
获取当前元素样式值:元素.currentStyle.样式名 IE浏览器高光时刻
getComputedStyle(元素,null).样式名 其他浏览器
<script>
// obj:元素 name:样式名 获取元素对应的样式值
function getStyle(obj,name){
if(window.getComputedStyle){
return getComputedStyle(obj,null)[name]
}else{
return obj.currentStyle[name] //IE专属
}
}
</script>
事件对象
当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参event传递进响应函数
盒子随着鼠标移动而移动 clientX/Y pageX/Y 相对于可视窗口/整个页面的鼠标位置
事件参数的兼容
event = event||window.event
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#box {
width: 200px;
height: 200px;
background-color: cadetblue;
position: absolute;
}
</style>
</head>
<body>
<script type="text/javascript">
window.onload = function () {
var box = document.querySelector('#box')
document.onmousemove = function (event) {
var left = event.clientX
var top = event.clientY
box.style.left = left + 'px'
box.style.top = top + 'px'
}
}
</script>
<div id="box"></div>
</body>
</html>
事件冒泡
所谓冒泡指的就是事件的向上传导,当后代元素的事件被触发时,其祖先元素的相同事件也会被触发
取消事件冒泡
event.cancelBubble = true
event.target 当前触发事件的对象
事件委派
要实现只绑定依次事件,即可应用到多个元素上,即使元素是后添加的
--将事件绑定到元素的共同的祖先元素,后代元素事件通过冒泡传递给祖先元素
--祖先元素使用event.target.className=='name'接受期望元素绑定,其余不绑定
--减少绑定次数,提高程序性能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
ul{
background-color:cadetblue;
}
</style>
</head>
<body>
<button id="btn">点击添加</button>
<ul>
<li><a href="#" class="link">超链接1</a></li>
<li><a href="#" class="link">超链接2</a></li>
<li><a href="#" class="link">超链接3</a></li>
</ul>
<script type="text/javascript">
window.onload = function () {
let btn = document.querySelector('#btn')
var ul = document.querySelector('ul')
btn.onclick = function () {
var li = document.createElement('li')
li.innerHTML = `<a href='#' class='link'>新建</a>`
ul.appendChild(li)
}
ul.onclick = function (e) {
if (e.target.className == 'link') {
alert('你好啊')
}
}
}
</script>
</body>
</html>
拖拽事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#box {
width: 200px;
height: 200px;
background-color: cadetblue;
position: absolute;
}
</style>
</head>
<body>
<div id="box"></div>
<script type="text/javascript">
window.onload = function () {
let box = document.querySelector('#box')
box.onmousedown = function (e) {
// 实现鼠标拖动点在点击处点
var ol = e.clientX - box.offsetLeft
var ot = e.clientY - box.offsetTop
document.onmousemove = function (e) {
var left = e.clientX - ol
var top = e.clientY - ot
box.style.left = left + 'px' //记得单位
box.style.top = top + 'px'
}
document.onmouseup = function () {
// 取消鼠标移动事件 就固定下来
document.onmousemove = null
document.onmouseup = null
}
}
}
</script>
</body>
</html>
键盘事件
String.fromCharCode(数字) 键盘AC码转为字符
<script type="text/javascript">
document.onkeydown=function(e){
//String.fromCharCode(数字) 数字转字符
console.log(String.fromCharCode(e.keyCode));
}
</script>
方块随着方向键移动练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#box {
width: 200px;
height: 200px;
background-color: cadetblue;
position: absolute;
}
</style>
</head>
<body>
<div id="box"></div>
<script type="text/javascript">
window.onload = function () {
var box = document.querySelector('#box')
document.onkeydown = function (e) {
event = event || window.event
var speed = 10
if (e.ctrlKey) {
speed = 50
}
switch (e.keyCode) {
case 38:
box.style.top = box.offsetTop - speed + 'px'
break
case 37:
box.style.left = box.offsetLeft - speed + 'px'
break
case 40:
box.style.top =box.offsetTop + speed + 'px'
break
case 39:
box.style.left = box.offsetLeft + speed + 'px'
}
}
}
</script>
</body>
</html>
BOM
window:整个窗口
navigator:窗口信息
location:导航条信息
history:前进后退键信息
screen:屏幕信息
Json
本质就是特殊格式的字符串使得所有语言都能识别和转化,主要用于数据交互
Json对象 '{"属性名1":"属性值1","属性名2":"属性值2"......}' 属性名值都要用双引号包裹
Json数组 '[1,2,3,4.....]'
方法: Json.parse(Json对象) 将Json对象转化为Js对象
Json.stringfy(Js对象) 将Js对象转化为Json对象
eval函数 能够用来执行一段字符串形成的js代码,并返回结果
如果eval中会将{}识别为代码块 用()包裹就不会被识别为代码块
<script type="text/javascript">
let str = "alert('hello')"
eval(“("+str+")”) //弹出hello
</script>
JavaScript高级
数据类型
typeof 返回数据类型的字符串表示 不能判断null和object object和array
<script type="text/javascript">
let str = 'xy'
console.log(typeof str === 'string') //true
console.log(typeof str === String) //false
</script>
A instanceof B A是否为B的实例 如果B函数的显示原型对象在A对象的原型链上则为true 反之false
VC <script type="text/javascript">
let obj = { name: 'wkq', age: 23 }
console.log(obj instanceof Object); //true
</script>
undefined和null的区别
undefined是定义但未赋值,null是定义赋值但值为null
何时使用null
初始时,对象赋值为null,用null表示将要赋值为对象
结束时 对象赋值为null 让对象成为垃圾对象被处理
数据(堆数据)|内存|变量(栈索引)
1、数据是存储在内存代表特定信息的东东
例如:var a = '基本数据/对象|方法/变量' a保存的是数据/对象|方法地址/变量内存内容
2、内存是内存条通电后产生的可存储数据的空间 断电后空间和数据都消失
3、变量=变量名+变量值 对应一块小内存 变量名用来查找对应的内存 变量值就是内存中保存的数据
回调函数
定义了 没有调用 最终执行了
IIFE 函数立即执行表达式|匿名函数自调用
作用:隐藏实现,不会污染外部命名空间 可以暴露方法
<script type="text/javascript">
// 函数自调用
(function(){
var a =3
console.log(a); //3
})()
var a = 4
console.log(a); //4
// ;使得不会认为是整体
;(function(){
var a =1
function test(){
console.log(++a);
}
window.$ = function(){
return {
test:test //返回对象 对外暴露test方法
}
}
})()
$().test()
</script>
原型
*每个函数定义时自动添加prototype属性,默认指向原型对象(空的object)
*原型对象中有个属性constructor,指向函数对象
<script type="text/javascript">
console.log(Date.prototype.constructor === Date) //true
</scripWt>
*可以给原型对象添加属性(方法)
*每个对象创建时自动添加 proto 属性,默认值为构造函数的prototype属性值
<script type="text/javascript">
function Fn(){
}
let fn = new Fn()
console.log(Fn.prototype===fn.__proto__); //true
</script>
原型链
对象要使用某个属性或方法,会现在自身寻找→原型对象中寻找→原型对象的原型中寻找......直到object对象原型
原型继承
构造函数的实例对象自动拥有构造函数原型对象的属性和方法,利用的就是原型链
实例的隐式原型等于构造的显示原型 二者指向同一个原型对象(空的object)
如果显示原型重新指向,会断开之前指向的原型对象,指向新的原型对象。而隐式原型对象依然指向之前的原型对象,不会随着显示原型的重新指向而改变指向
<script type="text/javascript">
function A() {}
A.prototype.n = 1
var b = new A()
A.prototype ={
n:2,
m:3
}
var c = new A()
console.log(b.n,b.m,c.n,c.m); //1 undefined 2 3
</script>
原型链上没有 实例的隐式原型→Function的显示原型的路径 如果实例访问Function上的方法 会报错
<script type="text/javascript">
function F(){
Object.prototype.a = function(){
console.log('a()');
}
Function.prototype.b = function(){
console.log('b()');
}
}
var f = new F()
f.a()
// f.b()
F.a()
F.b()
console.log(F);
console.log(Object.prototype);
console.log(Function.prototype); // ƒ () { [native code] }
console.log(Function instanceof Object); //true 函数是对象的实例
</script>
变量提升与函数提升 先执行变量提升再执行函数提升
变量提升
<script type="text/javascript">
var a =3
function fn(){
console.log(a);
var a =4
}
fn() //undefined
</script>
相当于
<script type="text/javascript">
var a =3
function fn() {
var a
console.log(a)
a = 4
}
fn() //undefined
</script>
函数提升
<script type="text/javascript">
fn() //fn() 函数提升
function fn(){
console.log('fn()');
}
</script>
执行上下文
当前代码运行环境,执行代码需要哪些数据提前准备好再开始执行
<script type="text/javascript">
/* 测试一 */
console.log('global begin:' + i) //undefined
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)
/* 测试二 */
var c = 1
function c(c) {
console.log(c)
}
c(2) //报错
//相当于
var c
function c(c) {
console.log(c)
}
c=1
c(2) //此时c=1 不是一个方法所以报错
/* 测试三 */
if (!(b in window)) {
var b = 1
}
console.log(b) //undefined
</script>
作用域和作用域链
作用域只能向外查找,不能向内查找。当需要某个属性时,向外找最近的祖先作用域里的同名属性
<script type="text/javascript">
var a =10,b=20
function fn(x){
var a = 100,c=300
console.log('fn()',a,b,c,x);
function bar(x){
var a =1000,d=400
console.log('bar()',a,b,c,d,x);
}
bar(100) //1000,20,300,400,100
bar(200) //1000,20,300,400,200
}
fn(10) //100,20,300,10
</script>
函数调用函数的情况下,考虑被调用函数的作用域向外
<script type="text/javascript">
var x =10
function fn(){
console.log(x);
}
function show(f){
var x =20
f()
}
show(fn) //10
</script>
对象属性名 不包括在函数作用域内
<script type="text/javascript">
var fn = function () {
console.log(fn)
}
fn() //fn()
var obj = {
fn2: function () {
console.log(fn2)
},
}
obj.fn2() //错误 函数内部作用域并没有fn2
</script>
闭包
定义:就是包含被引用变量的对象
嵌套情况下 内部函数引用了外部函数或变量时,产生闭包
将函数作为另一个函数的返回值 产生闭包
将函数作为实参传递给另一个函数调用 产生闭包
<body>
<button>按钮一</button>
<button>按钮二</button>
<button>按钮三</button>
<script type="text/javascript">
var btns = document.getElementsByTagName('button')
for (let i = 0,length =btns.length; i < length; i++) {
btns[i].addEventListener('click', function () {
alert('第'+(i+1)+'按钮被按了') //捕获i i在function之外
})
}
</script>
</body>
作用
使用函数内部的变量在函数执行完后,仍然存活在内存中(延长局部变量的生命周期
让函数外部可以操作到函数内部(局部变量)的数据
生命周期
产生:嵌套内部函数定义完成后产生
死亡:包含闭包的函数对象成为垃圾对象时
自定义JS模块
index.js
(function mymodule(){
var xy ='wkqXY'
function toUp(){
console.log('toUP'+xy.toUpperCase());
}
function toLow(){
console.log('toLow'+xy.toLowerCase());
}
window.mymodule={ //放在window上 导入该js文件即可使用
toUp,
toLow
}
})()
index.html
<script src="./index.js"></script>
<body>
<script type="text/javascript">
mymodule.toUp()
mymodule.toLow()
</script>
</body>
缺点
内存泄露
函数执行完后,函数内的局部变量没有释放,占用内存时间会变长 解决:及时释放
内存泄露多了就会导致内存溢出
意外的全局变量/定时器没有清理/闭包都会导致内存泄露
内存溢出
当程序运行需要的内存超过了剩余的内存,就会抛出内存溢出的错误
秃顶题
<script type="text/javascript">
function fun(n,o){
console.log(o);
return {
fun:function(m){
return fun(m,n)
}
}
}
var a = fun(0) //undefined
a.fun(1)
a.fun(2)
a.fun(3)
var b =fun(0).fun(1).fun(2).fun(3)
var c =fun(0).fun(1)
c.fun(2)
c.fun(3)
</script>
返回一个对象的函数就是工厂函数
进程和线程
进程:程序的一次执行,它会占用一片独有的内存空间 进程之间相互独立
多进程:一应用程序可以同时启用多个实例运行
线程:是进程内的一个独立执行单元,CPU最小的调度单元
单线程:
优:顺序编程简单易懂 缺:效率低
多线程:
优:提升CPU利用率 缺:创建多线程开销/线程间切换开销/死锁和状态同步问题
关于JS
JS是单线程运行的,
Js引擎执行代码的基本流程
1、先执行初始化代码
*设置定时器
*绑定监听
*发送网络请求