判断对象是否为空
判断数组为空
判断一个对象里面是否有某个key
- 可以直接访问这个key 如果得到的值为undefined代表没有 如果得到的值不为undefined代表有
- in属性
- 键名 in 对象名
- 有这个键返回true
- 没有这个键返回false
- hasOwnProperty
- 对象名.hasOwnProperty(键名)
- 有这个键返回true
- 没有这个键返回false
Object.defineProperty方法
- 元编程:对于原生js里面的功能进行编程
- 对于对象的功能进行编程(拦截)
- Object.defineProperty(对象, 键名, 修饰属性)
- 缺点:
- 如果数组或者对象里面的数据比较多的时候,需要遍历取给每一项数据添加get和set方法(性能差)
- 数组和对象后来添加的数据无法识别 不会触发set和get方法(无法支持后面新增的数据)
Proxy方法
- 是以面向对象方式封装的
代理的数据 = new Proxy(源数据, {
get(){
// 获取数据时候触发
},
set(){
// 设置数据的时候触发
}
})
闭包
-
什么是闭包
-
闭包在开发中的使用
-
闭包有哪些优缺点
-
函数
- 含义:将一段代码以命名的方式存储到一个盒子里面,在要执行这段代码的时候,需要根据这个名称找到这个盒子,将代码取出来运行
- 两个阶段
- 定义阶段
- 调用阶段
-
函数在定义阶段做的事件
function 函数名(){ // 一段代码 var a = 1 console.log(1) console.log(2) }
- 当代码读取到function的时候会在内存里面开辟一块空间,得到一个空间地址
- 当我们写一个函数名的时候,会把这个空间地址赋值给函数名
- 当我们在花括号里面写一段代码,会把这一段代码转换成字符串存储到上面开辟的空间里面
- 注意事项
- 函数在定义的时候函数体内的代码不会被执行
- 函数名存储的是一个地址,两个一模一样的函数肯定不相等除非函数名一样
-
函数的调用阶段
- 函数名()
- 当js读取到函数的时候,会根据函数名存储的地址找到存的代码
- 当js读取到小括号,会将存储的字符串代码转换成js,然后执行
-
函数的执行空间
函数在调用的时候会开辟一块空间 用于存储函数内部的定义的属性和方法,这个开辟的空间被称为函数的执行空间
函数调用结束之后,执行空间就会被自动销毁
-
闭包
- 含义:访问一个函数内部的局部变量
- 创建一个不销毁的执行空间?
- 当函数调用返回一个引用的数据类型
- 函数外部有一个变量使用着函数的返回值
- 实现
- 一个函数A里面有一个变量a,在函数A里面返回一个函数B
- 在函数A的外面有一个变量data使用着函数A的返回值,这个data其实就是函数B
- 调用data其实就是调用函数B,B里面用了一个变量a
实例
(以下代码均为课程实例)
(1)判断数组是否为空
<script>
var arr = [1,2,3]
var arr01 = [,,]
var arr02 = new Array(10)
var arr03 = []
console.log(arr)
console.log(arr03)
console.log(arr01 === [])
console.log(arr03 === []) // 因为他们不是同一个地址
// 判断数组为空一般根据数组的长度
console.log(arr01.length === 0)
console.log(arr03.length === 0)
</script>
(2)判断对象是否为空
<script>
var obj = {
a:1,
b:2,
c:3
}
var obj02 = {}
var obj03 = new Object()
// 对象里面没有length属性
console.log(obj)
console.log(obj02)
console.log(obj02 === {}) // 不是同一个地址 即使为空也是不相等
console.log(obj.length === 0)
console.log(obj02.length === 0)
// 把对象转换成json字符串
console.log(JSON.stringify(obj) === '{}')
console.log(JSON.stringify(obj02) === '{}')
console.log(JSON.stringify(obj03) === '{}')
// console.log('{}' == '{}')
// 利用for..in
for(let key in obj){
// 如果对象有键值对 它至少会进来一次
console.log(1)
}
for(let key in obj02){
// 如果是一个空对象 一次都不会进来
console.log(2)
}
/*
封装一个判断对象是否为空的方法
*/
function isEmptyObject(data){
let isEmpty = true // 默认为true
for(let key in data){
isEmpty = false
}
// 需要返回值
return isEmpty
}
console.log(isEmptyObject(obj)) // false
console.log(isEmptyObject(obj02)) // true
// 利用Object.keys() es6
console.log(Object.keys(obj))
console.log(Object.keys(obj02))
function isEmptyObject02(data){
return Object.keys(data).length === 0
}
console.log(isEmptyObject02(obj)) // false
console.log(isEmptyObject02(obj02)) // true
// 利用Object.getOwnPropertyNames() es5
console.log(Object.getOwnPropertyNames(obj))
console.log(Object.getOwnPropertyNames(obj02))
</script>
(3)判断对象里面是否含有某个键值对
<script>
var person = {
username: 'jack',
age: 18,
gender: '男'
}
var person02 = {
username1: 'jack',
age: 18,
gender: '男'
}
var person03 = {
username2: 'jack',
age: 18,
gender: '男',
score: undefined
}
/*
封装一个方法,判断对象里面是否有username
访问对象里面有这个key 返回的就是对应的value
访问对象里面一个不存在的key 返回的是undefined
*/
console.log(person.username)
console.log(person02.username)
function hasKey(obj,key){
// 获取这个对象的value 判断这个value是否为undefined
// 如果是undefined 代表对象里面没有这个键
// 如果不是undefined 代表对象里面有这个键
return obj[key] !== undefined
}
console.log(hasKey(person, 'username')) // true
console.log(hasKey(person02, 'username')) // false
console.log(hasKey(person03, 'username')) // false
console.log(hasKey(person03, 'age'))
console.log(hasKey(person03, 'score')) // false
// in属性
// 一个对象可以使用in来检测是否有这个键名
// 如果对象有这个键返回true
// 如果对象没有这个键返回false
// 用法 键名 in 对象
console.log('username' in person) // person有没有username这个键名
console.log('score' in person) // person有没有score这个键名
function hasKey(obj,key){
return key in obj
}
console.log(hasKey(person, 'username')) // true
console.log(hasKey(person02, 'username')) // false
console.log(hasKey(person03, 'username')) // false
console.log(hasKey(person03, 'age'))
console.log(hasKey(person03, 'score')) // true
// hasOwnProperty
console.log(person.hasOwnProperty('username'))
console.log(person.hasOwnProperty('score'))
function hasKey(obj,key){
return obj.hasOwnProperty(key)
}
</script>
(4)Object.defineProperty
<script>
var person = {
username: 'jack',
age: 18,
gender: '男'
}
// 对于对象person里面的username进行修改
Object.defineProperty(person, 'username', {
// value: '哈哈哈', // 对于person里面username的值进行了修改
// writable: false, // person里面的username不允许被修改了
// enumerable: false, // person里面username不可以被遍历
// configurable: false // person里面的username不可以删除
// get: function(){
// // 该方法在对象访问username的时候会触发一次
// console.log('get方法触发了')
// // 需要有一个返回值 这个返回值就是获取的value
// // 如果没有返回值 得到的value是undefined
// return 'rose'
// },
set(newVal){
// 该方法是在设置username的时候会触发一次
// 该方法会有一个形参 这个形参就是要设置的值
console.log('set方法触发了')
console.log(newVal)
}
})
console.log(person.username)
person.username = '哈哈哈'
console.log(person.username)
// delete person.username
// console.log(person)
// person.username = 'rose'
// console.log(person.username)
// for(let key in person){
// console.log(key)
// console.log(person[key])
// }
/* // 访问
console.log(person.username) // 访问对象里面某个value
// 遍历
for(let key in person){
console.log(key)
console.log(person[key])
}
// 修改
person.username = 'rose'
console.log(person.username)
// 删除
delete person.gender
console.log(person) */
</script>
(5)Object.defineProperty的get和set应用
<script>
var person = {
username: 'jack',
age: 18,
gender: '男'
}
/* 利用Object.defineProperty保证对象正常使用的同时还可以知道对象的值什么时候被获取,什么时候被设置 */
var tmp = person.username
Object.defineProperty(person, 'username', {
get: function(){
// 获取person的username时候触发
console.log('get触发了')
// 此时返回值又获取了username 又会调用get方法 这时候会出现死递归
// return person.username
return tmp
},
set: function(newVal){
console.log('set触发了')
console.log(newVal)
tmp = newVal
}
})
console.log(person.username)
person.username = 'rose'
console.log(person.username)
</script>
(6)双向数据绑定原理1
<body>
<div id="app"></div>
<div class="red"></div>
<script>
var person = {
age: 18,
};
// 把数据显示到页面
document.getElementById("app").innerHTML = person.age;
document.querySelector(".red").innerHTML = person.age;
// 修改数据
setInterval(() => {
person.age++;
// 每一次数据修改都需要重新渲染页面
document.getElementById("app").innerHTML = person.age;
document.querySelector(".red").innerHTML = person.age;
}, 1000);
</script>
</body>
(6)双向数据绑定原理2
<body>
<div id="app"></div>
<div class="red"></div>
<script>
/*
双向数据绑定:
单向: 数据改变,视图自动更新把一个数据显示到页面 数据变化了 页面会自动更新
利用的是Object.defineproperty的set方法
*/
var person = {
age: 18,
};
var tmp = person.age
Object.defineProperty(person, 'age', {
get:function(){
console.log('get被触发')
return tmp
},
set: function(newVal){
console.log('set被触发')
console.log(newVal)
// 修改person里面的age
tmp = newVal
// 除了改变数据之外还要去渲染视图
document.getElementById("app").innerHTML = newVal;
document.querySelector(".red").innerHTML = newVal;
}
})
// 把数据显示到页面
document.getElementById("app").innerHTML = person.age;
document.querySelector(".red").innerHTML = person.age;
// 对于person的age进行拦截 知道它什么时候被设置 什么时候被获取
// 修改数据
setInterval(() => {
// 只需要修改数据 无需再次更新页面
person.age = Math.random();
}, 1000);
person.age++
</script>
</body>
(6)双向数据绑定原理3
<body>
<div id="app"></div>
<div class="red"></div>
<input type="text" id="input" />
<button id="change">改变</button>
<script>
/*
双向数据绑定:
数据改变,视图自动更新:把一个数据显示到页面 数据变化了 页面会自动更新
利用的是Object.defineproperty的set方法
视图更新 数据也会更新
*/
var person = {
age: 18,
};
var tmp = person.age;
Object.defineProperty(person, "age", {
get: function () {
console.log("get被触发");
return tmp;
},
set: function (newVal) {
console.log("set被触发");
console.log(newVal);
// 修改person里面的age
tmp = newVal;
// 除了改变数据之外还要去渲染视图
document.getElementById("app").innerHTML = newVal;
document.querySelector(".red").innerHTML = newVal;
document.getElementById("input").value = newVal;
},
});
// 把数据显示到页面
document.getElementById("app").innerHTML = person.age;
document.querySelector(".red").innerHTML = person.age;
document.getElementById("input").value = person.age;
// 对于person的age进行拦截 知道它什么时候被设置 什么时候被获取
// 修改数据
// setInterval(() => {
// // 只需要修改数据 无需再次更新页面
// person.age = Math.random();
// }, 1000);
document.querySelector("#change").onclick = function () {
person.age++;
};
/* 视图变化数据更新 */
document.getElementById("input").oninput = function(){
// 获取到它的value值 赋值给person的age
person.age = this.value
}
</script>
</body>
(7)Vue使用
<!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>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<span>{{ message }}</span>
<input type="text" v-model="message">
<button @click="changeMessage">添加</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
message: "Hello Vue!",
},
methods: {
changeMessage(){
this.message = '哈哈哈'
}
},
});
</script>
</body>
</html>
(8)Object.defineProperty的缺点
<body>
<ul>
</ul>
<script>
var person = {
username: 'jack',
age: 17,
gender: '男',
score: 100
}
// 取出对象的所有的key 给每个key都添加get和set
for(let key in person){
Object.defineProperty(person, key, {
get(){
console.log('get触发了')
},
set(){
console.log('set被触发了')
}
})
}
console.log(person.score)
person.gender = '女'
// 无法支持新增的数据
person.aa = 1
var list = ['猪八戒', '孙悟空', '唐三藏', '沙和尚']
for(let i = 0;i<list.length;i++){
let li = document.createElement('li')
li.innerText = list[i]
document.querySelector('ul').appendChild(li)
Object.defineProperty(list, i, {
get(){
console.log('数组的get')
},
set(){
console.log('数组的set')
}
})
}
list[0] = '天蓬元帅'
// 无法支持新增的数据
list.push('白骨精')
list[list.length-1] = '咕咕'
</script>
</body>
(9)proxy方法
<body>
<script>
var person = {
username: 'jack',
age: 18,
gender: '男'
}
// 返回一个代理后的数据
var proxyPerson = new Proxy(person, {
get(){
console.log('get触发了')
},
set(){
console.log('set触发了')
}
})
console.log(proxyPerson)
console.log(person == proxyPerson)
// 只要修改和访问这个代理的数据 就会触发set和get
// proxyPerson.username
// proxyPerson.age
// proxyPerson.gender
// proxyPerson.username = 'rose'
// proxyPerson.age = 19
// proxyPerson.gender = '女'
// 新增一个数据
person.score = 100
proxyPerson.score
proxyPerson.score = 200
console.log(person)
console.log(proxyPerson)
// 代理数组
var list = ['猪八戒', '孙悟空', '唐三藏', '沙和尚']
var proxyList = new Proxy(list, {
get(){
console.log('数组get触发了')
},
set(){
console.log('数组set触发了')
}
})
console.log(proxyList[0])
proxyList[1] = '哈哈哈'
// 新增一个数据
list.push('李逵')
console.log(proxyList[proxyList.length-1])
proxyList[proxyList.length-1] = '葵葵'
</script>
</body>
(10)proxy语法
<script>
var person = {
username: 'jack',
age: 18,
gender: '男'
}
// 返回一个代理后的数据
var proxyPerson = new Proxy(person, {
// get方法有
get(target, key, receiver){
// 需要有一个返回值 这个返回就是得到的属性值
console.log('get触发了')
// console.log(target) // 原数组
// console.log(key) // 访问的key
// console.log(receiver) // 代理后的数据
return target[key] // 取出源数据的属性值返回
},
set(target, key, newVal, receiver){
console.log('set触发了')
// 设置的时候要设置源数据
target[key] = newVal
}
})
console.log(proxyPerson.username) //
console.log(proxyPerson.age) //
proxyPerson.age = 20
console.log(proxyPerson.age)
</script>
(11)双向数据绑定
<body>
<div id="app">
<p class="username"></p>
<p class="age"></p>
<p class="gender"></p>
<input type="text" id="input">
<button onclick="click()">按钮</button>
</div>
<script>
var person = {
username: 'jack',
age: 18,
gender: '男'
}
let proxyPerson = new Proxy(person, {
get(target,key,receiver){
return target[key]
},
set(target,key,val,receiver){
console.log('set触发了')
target[key] = val
// 修改数据的时候 更新视图
document.querySelector('.username').innerHTML = person.username
document.querySelector('.age').innerHTML = person.age
document.querySelector('.gender').innerHTML = person.gender
document.getElementById('input').value = person.age
}
})
// 第一次渲染视图
document.querySelector('.username').innerHTML = person.username
document.querySelector('.age').innerHTML = person.age
document.querySelector('.gender').innerHTML = person.gender
document.getElementById('input').value = person.age
// 单向数据绑定 数据变化 视图会自动更新
function click(){
proxyPerson.username = 'rose'
proxyPerson.age = Math.random()
}
// 视图更新 数据也会变化
document.getElementById('input').oninput = change
function change(){
console.log('input...')
console.log(this)
proxyPerson.age = this.value
}
</script>
</body>
(12)函数定义阶段
<body>
<div class="box">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Rem amet, praesentium recusandae aut, repellendus vel doloribus laudantium qui reprehenderit saepe eius odit corrupti fugit labore quo, autem enim. Consequatur, laboriosam.
</div>
<script>
// 函数在定义的时候 函数体内不会执行
function getSum(){
var a = 1
console.log(a)
console.log(2)
console.log(3)
}
function fn(){}
function foo(){}
// fn和foo存储都是地址
console.log(fn == foo)
var box = document.querySelector('.box')
function doClick(){
console.log(1)
}
box.addEventListener('click', doClick)
box.removeEventListener('click', doClick)
</script>
</body>
(13)函数调用阶段
<body>
<div class="box">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Rem amet, praesentium recusandae aut, repellendus vel doloribus laudantium qui reprehenderit saepe eius odit corrupti fugit labore quo, autem enim. Consequatur, laboriosam.
</div>
<script>
// 函数在定义的时候 函数体内不会执行
function getSum(){
var a = 1
function fn(){
console.log('fn被调用')
}
console.log(a)
console.log(2)
console.log(3)
fn()
}
// 函数的调用阶段
// 会将之前存储的字符串代码 转换成 js代码执行
// 函数内的定义变量 定义方法需要有一个地方进行存储
// 函数在调用的时候会开辟一块空间 用于存储函数内部的定义的属性和方法,这个开辟的空间被称为函数的执行空间
// 这个执行空间在我们函数调用结束之后就会自动销毁
getSum() // 在getSum调用的时候会开辟一块执行空间
// getSum调用结束 调用结束执行空间就会自动销毁
</script>
</body>
(14)闭包
<script>
function foo(){
var a = 1
// 闭包返回的是一个函数
// 函数是引用的数据类型
return function A(){
console.log(a)
a++
}
}
var data = foo() // 会生成一个执行空间 在执行空间内部存储a
// foo调用结束之后 执行空间被销毁了 a就消失了
// 闭包是实现在foo外面访问foo内部的a
// data就是函数foo返回的函数A
console.log(data)
// 调用data其实就是调用A A函数内的代码就会执行 A函数用了一个变量a本身没有 就会自动往上一级作用域查找
// 上一级作用域foo 里面有一个变量a 用的就是foo里面的a
data()
data()
data()
data()
function A(){
var a = 1
return function B(){
console.log(a)
}
}
var data = A()
data()
</script>
(15)创建不销毁的执行空间
<script>
// 当函数调用返回一个引用的数据类型
function foo(){
var obj = {
a:1
}
return obj
}
foo()
// 函数外部有一个变量使用着函数的返回值
var data = foo()
// 在foo调用结束之后 使用data data就是函数foo的返回值 其实就是obj obj存储在函数foo的执行空间里面
// 能用data.a 就说明函数foo在调用之后执行空间并没有被销毁
console.log(data.a)
</script>
(16)闭包作用
<script>
// 闭包是为了避免全局变量污染的问题
// 有一个变量需要在函数外部使用 但是如果把这个变量放在全局 有可能被别人覆盖
function foo(){
var a = 1
return function(){
console.log(a)
a++
}
}
var data = foo()
data()
// 污染了a
var a = '哈哈哈'
data()
data()
</script>