控制一些持续触发的事件执行的时机的一种方法。 比如: resize, scroll, mousemove等。
防抖(debounce)
防抖: 触发事件后指定时间内其函数只执行一次, 若在指定的时间内又触发了事件,则会重新计算函数的执行时间。(防抖函数也可分为:立即执行和非立即执行两种类型)
function debounce(fun, wait) {
let timeout
return function() {
let context = this;
let args = arguments;
!!timeout && clearTimemout(time)
timeout = setTime(()=>{
fun.apply(context, args)
}, wait)
}
}
非立即执行: 在事件触发后等待自定义时间后才执行, 在等待时间内如果又触发了事件,则清空之前的计时器并重新计算函数执行时间。
function debounce(fun, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
!!timeout && clearTimeout(timeout);
let callNow = !timeout;
timeout = setTimeout(()=>{
timeout = null;
}, wait)
!!callNow && fun.apply(context, args)
}
}
立即执行: 触发函数后立即执行, 然后在设定的时间内不会触发事件。当超过设定时间时计时器将会重置计时器,允许后续第一个调用函数的事件执行。
节流(throttle)
节流: 连续触发事件但在指定时间内,只执行一次函数。 节流可以稀释函数的执行频率。(节流包含两种实现:时间戳和定时器)
function throttle (func, wait){
var previous = 0;
return function(){
let now = Date.now();
let context = this;
let args=arguments;
if(now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
在持续触发事件的过程中,函数会立即执行且每个自定义时间执行一次。
function throttle(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
该方式在持续触发事件的过程中, 函数不会立即执行,并且每个指定时间执行一次,在停止触发事件后,函数还会再执行一次
堆和栈的区别
其实深拷贝和浅拷贝的主要区别就是其在内存中的存储类型不同。堆和栈都是内存中划分出来用来存储的区域
栈(stack) 为自动分配的内存空间, 它由系统自动释放; 而堆(heap)则是动态分配的内存, 大小不定也不会自动释放。
深拷贝简单实现
function cloneShallow(source) {
var target = {}
for(var key in source) {
if(Object.prototype.hasOwnProperty.call(source. key)) {
target[key] = source[key];
}
}
return target
}
function cloneDeep(source) {
var target = {}
for(let key in source) {
if(Object.prototype.hasOwnProperty.call(source, key)) {
// typeof null === 'object'
if(!!source[key] && typeof source[key] === 'object) {
target[key] = cloneDeep(source[key])
} else {
target[key] = source[key]
}
}
}
return target
}
hasOwnProperty: 所有继承了Object 的对象都会继承到hasOwnProperty 方法。 该方法判断对象自身是否存在指定属性, 且该方法会忽略掉那些从原型上继承来的属性
// 一个用于生成唯一值的Symbol 方法
function mySymbol(obj) {
let unique = (Math.random() + new Date().getTime()).toString(32).slice(0, 8)
if(obj.hasOwnProperty(unique)) {
return mySymbol(obj)
} else {
return unique
}
}
Function.prototype.myCall = function(context) {
context = context || window
let fnName = mySymbol(context)
context[fnName] = this // 给context 添加一个方法,指向this (this就是 “ obj.fnName.myCall() ” 中的fnName方法)
// 处理参数, 去掉第一个参数this, 其它传入fn函数
let args = [...arguments].slice(1) // 把[...arguments] 转成一个数组, slice 返回一个新数组
let result = context[fnName](...args) // 执行fn
delete context[fnName] // //删除挂载在借用对象上的fn属性
return result
}
// 一个用于生成唯一值的Symbol 方法
function mySymbol(obj) {
let unique = (Math.random() + new Date().getTime()).toString(32).slice(0, 8)
if(obj.hasOwnProperty(unique)) {
return mySymbol(obj)
} else {
return unique
}
}
Function.prototype.myApply = function(context) {
context = context || window
let fnName = mySymbol(context)
context[fnName] = this // 给context 添加一个方法,指向this (this就是 “ obj.fnName.myCall() ” 中的fnName方法)
// 处理参数, 去掉第一个参数this, 其它传入fn函数
let args = [...arguments].slice(1) // 把[...arguments] 转成一个数组, slice 返回一个新数组
let result = context[fnName](...args) // 执行fn
delete context[fnName] // //删除挂载在借用对象上的fn属性
return result
}
特点: 函数调用改变this; 返回一个绑定this 的函数; 接收多个参数; 支持柯里化形式参数 fn(1)(2)
Function.prototype.bind = function(context) {
// 返回一个绑定this的函数, 我们需要在此保存this
let fnName= this; // 这里this 就是需要bind的函数
// 可以支持柯里化参数, 保存参数
let arg = [...arguments].slice(1)
// 返回一个函数
return function(){
let newArgs = [...arguments]
// 返回函数绑定this, 传入两次保存的参数
// 考虑返回函数有返回值做了return
return fnName.apply(context, arg.concat(newArgs))
}
}
// 使用:
let fn = Person.say.bind(Person1) // 返回一个绑定多个作用域的函数
fn()
fn(name, age, xxxx, .....)
JSONP(JSON with Padding)它是一个非官方的协议。其跨域利用script的src属性, 这个属性不受同源策略影响,可以访问不同服务下的资源。
// 客户端
function callback(data){
console.log(data);
}
var scriptDom = document.createElement('script');
scriptDom.src = "http://localhost:8082/getdata?cb=callback";
document.body.appendChild(scriptDom);
// server 端(基于express):
app.get('/getdata',function(req,res){
//同步读取json文件
var data = fs.readFileSync('server2/data.json').toString();
var qs = url.parse(req.url).query;
var cb = querystring.parse(qs).cb;
var jsonp = cb+"("+data+")";
res.send(jsonp);
}
如上例子允许后会在浏览器控制台输出: 服务端返回的 Object:{} 数据对象
// JSONP 实现
function JSONP({
url,
params,
callbackKey, // cb参数名
callback // 回调函数
}){
// 唯一id, 不存在则初始化
JSONP.callbackId = JSONP.callbackId || 1
params = params || {}
// 传递的callback 名,和厦门预留的一致
params[callbackKey] = `JSONP.callbacks[${JSONP.callbackId}]`
// 避免全局污染
JSONP.callbacks = JSONP.callbacks || []
// 按照id放置 callback
JSONP.callbacks[JSONP.callbackId] = callback
const paramsKeys = Object.keys(params)
const paramString = paramKeys.map(key => `${key}=${params[key]}`).json('&')
const script = document.createElement('script')
script.setAttribute('src', `${url}?${paramString}`)
document.body.appendChild(script)
// id 占用,自增
JSONP.callbackId ++
}
// 使用JSONP
JSONP({
url: 'http://xxxxx/ajax/jsonp/xxxxxx',
params: {
key: 'test1',
},
callbackkey: '_cb',
callback(result) {
console.log(result.data)
}
})
JSONP({
url: 'http://xxxxx/ajax/jsonp/xxxxxx',
params: {
key: 'test2',
},
callbackkey: '_cb',
callback(result) {
console.log(result.data)
}
})
JSONP 调用后通过控制台可以卡到请求都是:
http://xxxxxxx/ajax/jsonp/xxxxx?key=test1&_cb=JSON.callbacks[1]这样的,得到的 js 也是 JSON.callbacks1, 这样就避免回调命名冲突问题,也避免了全局域的污染
注: == 上面代码存在一个问题那就是参数部分如下情况:
params: {
a: ‘545&b=3’
b: ‘5’,
},
参数 a 中包含了b的值,其实用户这是希望a的值为: 545&b=3。 解决的办法,进行URI编码==, encodeURIComponent(‘trdgd&b=2’) 的结果为: trdgd%26b%3D2。
JSONP部分实现可改为:
const paramString = paramKeys
.map(key => `${key}=${encodeURIComponent(params[key])}`)
.join('&')
这里值得一提的是,由于最终的 URL 不能包含 ASCII 码以外的字符,所以其实当使用中文或者特殊字符时其实会被自动编码。而 +,空格,/,?,%,#,&,= 等字符在 URL 中则会出现歧义,只有手动编码后才能让服务器端正确解析
new 操作符都做了些什么? 可以用四步来总结:
模拟一个new方法的实现:
// 一个new 的实现
function myNew(){
// 创建一个空对象
let obj = new Object()
// 获取构造函数
let Constructor = [].shift.call(arguments);
// 链接到原型
obj.__proto__ = Constructor.prototype;
// 绑定this值
let result = Constructor.apply(obj.arguments); // 使用apply, 将构造函数中的this指向新对象, 这样新对象就可以访问构造函数中的属性和方法。
// 返回新对象
return typeof result === 'object' ? result : obj // 如果返回值是一个对象就返回该对象,否则返回构造函数的一个实例对象
}
// 使用测试
function Product(name, type) {
this.name = name;
this.type = type;
}
// 通过new 创建构造实例
let pro1 = new Product('礼盒', '水果');
console.log(pro1.name, pro1.type) // 礼盒, 水果
// 通过myNew方法创建实例
let pro2 = myNew(People, '礼盒', '水果');
console.log(pro2.name, pro2.type); // 礼盒, 水果
JavaScript 原型
ES2019规范是当前最新的语言规范,可以作为本主题的权威素材来源。
prototype chain 原型链
在ECMAScript2019规范里,只通过短短的一句话,就介绍完了prototype chain。
原型链概念只是在原型这个概念基础上所作的直接推论。
也就是说,prototype 对象也有自己的隐式引用,有自己的prototype对象。
如此, 构成了对象的原型的原型的原型的…链条, 直到某个对象的隐式引用为null, 整个链条终止
上图运转流程简述:
TCP连接与关闭原则流程
TCP三次握手
TCP四次挥手
HTML渲染流程运转机制
function quickSort(arr) {
if(arr.length<1) return arr;
var poivtIndex = Math.floor(arr.length/2)
var poivt = arr.splice(poivtIndex, 1)[0]
var left = []
var right = []
for(var i=0; i
未完待续…