1. 原型和原型链
1.1 原型
- js中,函数可以有属性,每个函数都有一个特殊的属性叫做原型prototype(原型)
1.2 原型链
- 当我们访问某个对象的属性时,如果该对象本身没有,就会到对象的原型中去寻找定义,如果原型也没有,就再向原型对象的原型中寻找,依此类推,如果最顶层的对象也没有,则返回undefined,整个查找过程为链式,连接对象和对象的原型的链条就叫做原型链
1.3 原型链存在的意义
- 可以使实例对象共享原型的属性和方法,节省代码量,减小内存的开销
2. 作用域
- 代码在运行时某些特定部分中的变量,函数,对象,代表对上述资源的可访问性
- 分类
- 全局作用域
- 在代码的任何地方都能访问到的变量,对象,函数
- 局部作用域
- 函数作用域
- 块级作用域
- {} 包裹起来的区域
- 全局作用域
2.1 作用域链
- 在某一个地方访问一个变量,函数,或对象时,首先在当前作用域中查找,如果没有找到,则向父级的作用域中进行查找,以此类推,如果最终没有找到,则报undefined的错误,这种一层层嵌套的关系,就叫做作用域链
2.2 作用域存在的意义
- 变量隔离,不同作用域下的同名变量互不干扰
3. 闭包
-
能够访问其他函数内部变量的函数,称为闭包
-
即一个函数在调用时,其内部找不到相应需要的变量或对象,需要在作用域链上寻找,找到后会维持对该变量的引用(一个函数和对其周围环境的引用捆版在一起)
-
产生的问题
- 维持对父级作用域变量的引用,会导致父级作用域中的变量常驻内存
-
使用的场景
-
防抖节流函数实现
-
事件函数的封装
-
循环注册事件函数:基本思想:如果循环变量i为全局变量,则循环注册最终事件处理函数引用的为同一个i
-
闭包:每个循环都是一个立即执行函数,函数里面注册事件,这里的i(for循环的i->立即执行函数形参->事件处理函数的i引用形参)
- 则事件处理函数与立即执行函数作用域的变量i的形成的闭包
-
隐藏变量
-
外层函数,类似java的类class,其中的局部变量==》java的private 变量
-
提供一些修改局部变量的方法,return 回去(setPrivateLoacl,getPrivateLocal)(目的是为了外层函数外,即全局作用域中可以通过方法进行修改)
-
return不是必须的,之所以return 无非是为了提供一个操作私有数据的接口
-
例如上一个循环注册事件,无需return
-
-
4. call,bind,apply 方法
- 相同点:都可以改变this的指向
- 不同点 :apply传递的第二个参数为数组(即如果有1个或多个参数,以数组的形式传递)
- call 和 apply改变this指向同时也会调用函数,返回值为函数的调用
- bind 返回的是改变this指向后的新函数
4.1 call的使用场景
- 判断数据类型
- Object.prototype.toString.call(this指向(调用者))
- 类数组(伪数组,对象表示的数组)转数组
- Array.prototype.slice.call(调用者)
4.2 apply的使用场景
- 关键,传递的第二个参数为数组
- 求最大值,最小值(Math.max(...array))
- 使用apply,(Math.max.apply(null,array))
4.3 bind的使用场景
-
改变this指向的同时不立即调用
-
事件处理的回调函数,因为事件处理函数直接调用,相当于
-
function callback(){ // 处理逻辑 }
-
function里面的this为undefined
-
this.callback=this.callback.bind(this) ,绑定this
-
// * 判断数据类型
// * 类数组转数组
const array = [1, 2, 3, 4];
const type = array.toString()
console.log(array);
// const type = Object.prototype.toString.call(array);
console.log(`type`, type)
// const arrayLike = {
// 0: "name",
// 1: "age",
// 2: "gender",
// length: 3
// }
// const res = Array.prototype.slice.call(arrayLike);
// console.log(`res`, res)
// // ["name","age","gender"]
// apply 对给定数组求最大值/最小值
// const array = [1,2,3,4,5];
// const max = Math.max.apply(null,array)
// const min = Math.min.apply(null,array)
// // Math.max(1,2,3,4,5)
// console.log(`max`, max)
// console.log(`min`, min)
// bind
// class App extends React.Component {
// constructor(props) {
// super(props);
// this.name = 'freemen'
// this.handleClick = this.handleClick.bind(this)
// }
// handleClick(){
// console.log(`this.name`, this.name)
// }
// render(){
// return (
// <button onClick={this.handleClick}>
// 点击
// </button>
// )
// }
// }
5.数组去重
5.1 前置知识
- 数组的indexOf()方法
- 参数数组项,返回值,数组项的下标
- filter方法
- 返回值新数组
- sort
- 参数(a,b),return a-b 从小到大排,b-a 从大到小排
- 返回值排序后的新数组
- push
- 参数:需要push进去的项
- 返回值:数组push后的长度
- 改变了原数组
5.2 filter +indexOf数组去重
array.filter((item,index)=>{
return array.indexOf(item)===index
})
- 原理
- for循环一遍数组
- 循环每一项时看该项是不是在之前出现过,即判断当前项的下标,与第一次出现相同项的下标是否相同
5.3 sort+移动零原理
// 先排序
array=array.sort()
let arr=[]
for(let i=0;i<array.length;i++){
if(array[i]!==array[i-1]){
arr.push(array[i])
}
}
- 原理
- 对原数组进行排序,从小到大
- 则相同的元素会成组出现,如111222333
- 我们需要从中获取到我们需要的部分
- 即每组的分界点的元素
- undefined和1
- 1和2
- 2和3
- 即每组的分界点的元素
5.4 Set + 解构赋值
[...newSet(array)]
- Set不允许出现重复元素
5.5 set+Array.from
Array.from(new Set(array))
对象数组去重(根据某个key)
- 临时对象缓存key值实现
- 存储key值
- 没有则存储一个新的key值
- 有则直接过滤掉
const array=[{name:'666',age:20},{name:'999',age:20}]
// 去除age相同的
let template={}
let result=[]
for(let i=0;i<array.length;i++){
if(template[array[i]['age']]){
continue;
}
template[array[i]['age']]=true;
result.push(array[i])
}
- reduce函数实现
const array=[{name:'666',age:20},{name:'999',age:20}]
// 去除age相同的
let template={}
array.reduce((prev,curr)=>{
if(!template[curr['age']]){
template[curr['age']]=true
prev.push(curr)
}
return prev;
},[])
- prev最初为[] 数组
6.数组最大值
- Math.max()
- Math.max(...array)
- Math.max.apply(array)
- Array.reduce()
- array.reduce((prev,curr)=>{
- return curr>prev? curr:prev
- })
- array.reduce((prev,curr)=>{
- sort+lastIndexOf()
- 排序后找最后一个
7.节流
- 单位事件内触发多次事件,第一次有效
- 场景
- 轮播图单位事件内多次被点击
- movemouse事件,获取鼠标的位置,控制鼠标样式图标跟随移动
<!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>
</head>
<body>
<script>
// 实现节流函数
function throttle(fn, timeOut) {
// 定义一个timer
let timer = null
// 返回一个函数调用,注意,这里形成的闭包
return (event) => {
if (timer) {
return
}
timer = setTimeout(function() {
// 计时结束后的逻辑
clearTimeout(timer)
timer = null
fn(event)
}, timeOut)
}
}
// 监听窗口resize事件
window.onresize = throttle((event) => {
console.log(event);
}, 1000)
</script>
</body>
</html>
8.防抖
- n秒内多次触发同一事件,最后一次有效
<!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>
</head>
<body>
<input type="text" id="q">
<script>
// 实现防抖
const debounce = (fn, timeOut) => {
let timer = null;
return () => {
// 每次触发,都会进入这里
// 清除原先的定时器,当不进入时,最后一个就生效了
clearTimeout(timer)
timer = null
// 形成了闭包
timer = setTimeout(function() {
// 指向搜索的逻辑
fn()
}, timeOut)
}
}
const search = document.getElementById('q')
search.oninput = debounce(() => {
console.log('saerch');
}, 1000)
</script>
</body>
</html>
9.数组拍平
-
数组降维
-
实现方法
- reduce函数
- es6的flat函数
- while循环+Array
9.1 reduce
- 原理:递归调用
// reduce函数
// function flatter(array) {
// return array.reduce(function(prev, current) {
// return prev.concat(Array.isArray(current) ? flatter(current) : current)
// }, [])
// }
9.2 flat函数
- Infinity为数组的维数,Infinity为最大值
array.flat(Infinity)
9.3 while循环+展开运算符
- 每次循环,判断当前数组是否存在数组元素
- 有就展开,然后再合并
while(array.some(Array.isArray)){
array=[].concat(...array)
}
10.实现new操作符
<!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>
</head>
<body>
<script>
// 构造函数
const TMap = function (options) {
this.name = options.name;
this.address = options.address;
}
// const map = new TMap({
// name: 'tmap',
// address:"BJ"
// });
// console.log('map :>> ', map);
//1. 实例化对象 2.返回值的问题: 构造函数中如果有值返回 那实例化后的对象就是这个返回值。
const ObjectFactory = (...args) => {
const obj = {};
// [].shift.call 等价于 Array.prototype.shift.call
// 因为args为伪数组
const Constructor = [].shift.call(args)
// 实例对象的原型链和构造函数的原型指向相同
obj.__proto__ = Constructor.prototype;
// obj.Constructor(args) Constructor==>Tmap
// this.name = options.name; ==>obj.name==args.name
// this.address = options.address; ==>obj.address=args.address
// 这里函数并无返回值,即Tmap没有写返回值,如果写了,返回的就不是obj这个对象了
const ret = Constructor.apply(obj, args);
console.log('ret', ret, obj);
return typeof ret === 'object' ? ret : obj;
}
const map = ObjectFactory(TMap, {
name: "MAP",
address: "BJ"
});
console.log('map :>> ', map);
</script>
</body>
</html>
11. 实现一个bind函数
function origin(a, b) {
console.log(this.name);
console.log([a, b]);
}
const obj = {
name: "freemen"
}
// const func = origin.bind(obj,2);
// func(1)
Function.prototype.bindFn = function() {
// 1 获取源函数
// this为函数的调用者,即origin()函数
const fn = this;
// 2 获取目标对象
// 获取第一个参数,即需要修改到的this指向
// const func = origin.bind(obj,2);
const obj = arguments[0];
// 3. 获取源函数参数列表
const args = [].slice.call(arguments, 1)
// 4. 返回函数
return function() {
// 5 获取返回函数的参数列表
// func(1)
// 即返回的新函数(this指向已经修改)在调用时传递的参数
const returnArgs = [].slice.call(arguments);
// 6 执行源函数
fn.apply(obj, args.concat(returnArgs));
}
}
12. apply 和 call函数的实现
- 原理,都是在目标对象obj(修改后this指向)中将调用的(需要修改指向)方法加入obj中
- obj的调用作为返回结果
const obj = {
name: "freemen"
}
function testFunc(a,b){
console.log('a :>> ', a);
console.log('b :>> ', b);
console.log('this.name :>> ', this.name);
}
// testFunc.call(obj,'a','b')
// testFunc.apply(obj,['a','b'])
const core = (context,args,_this) => {
args = args || [];
const key = Symbol();
context[key] = _this;
/*
obj={
key:testFunc
}
obj.key(args)
// 调用者是不是变成了obj this指向改变
*/
const result = context[key](...args)
delete context[key]
return result;
}
Function.prototype.callFn = function(context,...args){
// context 为this指向,obj对象
// this为方法的调用者 即testFunc
return core(context,args,this);
}
Function.prototype.applyFn = function(context,args){
return core(context,args,this);
}
testFunc.callFn(obj,'a','b');
testFunc.applyFn(obj,['a','b']);
//
13. instanseof 运算符
- 用于检测构造函数的prototype属性是否出现在实例对象的原型链上
- 实现原理
- 实例对象的原型链 __ proto __
- 构造函数的prototype显示原型
- 判断对象的 __ proto __ === prototype,找到返回,找不到,对象的原型链上移
function Persion(){
this.name = "freemen"
}
const obj = new Persion
// console.log(obj instanceof Persion)
// instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
function instance_of(Obj, Constructor){
let implicitPrototype = Obj.__proto__; // 获取实例对象的隐式原型
let displayPrototype = Constructor.prototype; // 获取构造函数的prototype 属性
// while 循环 -> 在原型链上不断向上查找
while(true){
// 直到 implicitPrototype = null 都没找到, 返回false
if(implicitPrototype === null){
return false
// 构造函数的 prototype 属性出现在实例对象的原型链上 返回 true
}else if (implicitPrototype === displayPrototype){
return true
}
// 在原型链上不断查找 构造函数的显式原型
implicitPrototype = implicitPrototype.__proto__
}
}
const has = instance_of(obj,Persion)
console.log(`has`, has)
标签:obj,函数,javascript,return,const,array,cnblog,name
From: https://www.cnblogs.com/lingxin1123/p/17092943.html