欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

致敬我们前端遇到的那些坑(2)---js篇

程序员文章站 2022-06-09 20:20:08
...

致敬我们前端遇到的那些坑(2)—js篇

1. 数据类型及判断

在 JavaScript 中有 6种不同的数据类型:
• string
• number
• boolean
• object //对象类型
• function
symbol //表示独一无二的值
2 个不包含任何值的数据类型:
• null 声明了变量为空值
• undefined 声明了变量,但没有赋值
检测数组的几种方式:
定义一个数组:
var arr1 = [1,2,3];
typeof arr1; //object

1、instanceof

2、isArray
这个方法也可以用来分辨数组和对象,但前提是必须知道是数组或者对象
Array.isArray([]); //true
Array.isArray({}); //false

3、constructor
arr1.constructor === Array; //true
4、利用原型方法
由于在iframe中创建的Array并不共享prototype,此时可以:
var obj=
Object.prototype.toString.call(obj)

2.三元表达式

• (关系表达式)?表达式1:表达式2;
• 如果条件为true,运算后的结果是表达式1;
• 如果条件为false,运算后的结果是表达式2;
B:示例:

  1. 获取两个数中大数。
  2. int x=3,y=4,z;
  3. z = (x>y)?x:y;//z变量存储的就是两个数的大数

3.js操作正则表达式

test方法,该方法用来测试某个字符串是否与正则匹配,匹配就返回true,否则返回false。
该方法接受一个字符串作为参数
代码:
var reg=/^text/;
console.log(reg.test(“text”));

4. JS中的事件委托(事件代理)

一步一步来说说事件委托(或者有的资料叫事件代理)

  1. js中事件冒泡我们知道,子元素身上的事件会冒泡到父元素身上。
  2. 事件代理就是,本来加在子元素身上的事件,加在了其父级身上。
  3. 那就产生了问题:父级那么多子元素,怎么区分事件本应该是哪个子元素的?
  4. 答案是:event对象里记录的有“事件源”,它就是发生事件的子元素。
  5. 它存在兼容性问题,在老的IE下,事件源是 window.event.srcElement,其他浏览器是 event.target
  6. 用事件委托有什么好处呢?
  7. 第一个好处是效率高,比如,不用for循环为子元素添加事件了
  8. 第二个好处是,js新生成的子元素也不用新为其添加事件了,程序逻辑上比较方便

例子1. 页面有个ul包含着4个li,鼠标移动到li上,li背景变成红色,移出,背景恢复原色。

5.jsonp原理

JSONP实现跨域请求的原理简单的说,就是动态创建

缺点:只能发送get请求

function getdata(data){
    console.log(data)
}

实例:
html:

js:
$.ajax({
url: ‘http://127.0.0.1:8001/list’,
method: ‘get’,
dataType: ‘jsonp’, //=>执行的是JSONP的请求
success: res => {
console.log(res);
}
});

服务器:
let express = require(‘express’),
app = express();
app.listen(8001, _ => { //_为了占位
console.log(‘OK!’);
});
app.get(’/list’, (req, res) => {
let {
callback = Function.prototype //解构并赋默认值是一个匿名空函数
} = req.query;
let data = {
code: 0,
message: ‘珠峰培训’
};
res.send(${callback}(${JSON.stringify(data)}));
});

6.闭包

用处:1.读取函数内部的变量;
2.这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。
3.扩展了变量的作用范围
优点:1:变量长期驻扎在内存中;
2:避免全局变量的污染;
3:私有成员的存在 ;
特性:1:函数套函数;
2:内部函数可以直接使用外部函数的局部变量或参数;
3:变量或参数不会被垃圾回收机制回收 GC;
缺点:
常驻内存 会增大内存的使用量 使用不当会造成内存泄露,详解:
(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
function makeFunc() {
var name = “Mozilla”;
function displayName() {
alert(name);
}
return displayName;
}

var myFunc = makeFunc();
myFunc();

// 经典面试题
function a() {
for(var i = 0 ; i< 10 ; i++) {
setTimeout(function() {
console.log(i)
}, 0)
}
} // 会输出10个10,要求输出1,2…10

// 方法1 将var改成let //let的块级作用域
// 方法2 闭包
function a() {
for (var i = 0; i < 10; ++i) {
setTimeout(
(function(i) {
return console.log(i)
})(i), 0);
}
}
a()

//1.使用闭包实现点击li,输出第几个
for (var i = 0; i < lis.length; i++) {
(function(i) {
lis[i].onclick = function() {
console.log(i)
}
})(i)
}

    //2.延时3秒,打印对应li中获得内容
    for (var i = 0; i < lis.length; i++) {
        setTimeout((function(i) {
            lis[i].onclick = function() {
                console.log(lis[i].innerHTML)
            }
        })(i), 3000)
    }

7.call(),apply(),bind()

1.call
1.立即执行函数
2.改变this指向
3.传参 .call(thisObj, arg1, arg2, arg3, arg4); //后面的参数都是实参
4.适用于继承
2.apply
1.立即执行函数
2.改变this指向
3.传参 .apply(thisObj, [args]); //后面的参数都是实参
4.有关数组的操作

<script>
    var arr = [1, 2, 3]
    var max = Math.max.apply(Math, arr)
    console.log(max)
</script>

函数内部通过arguments获取参数 //console.log(arguments)

3.bind()
1.不会立即执行函数
2.改变this指向
3.传参 .call(thisObj, arg1, arg2, arg3, arg4); //后面的参数都是实参
4.适用于不会立即调用但又需要改变this,比如定时器

<button>点击禁用3秒</button>
<script>
    var num = {}
    var btn = document.querySelector("button")
    console.log(btn)
    btn.onclick = function() {
        this.disabled = true
        setTimeout(function() {
                this.disabled = false
            }.bind(this), 3000) //this btn
    }
</script>

8. js中的this指向

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象(一般情况下)
如果函数没有调用者,this就是window

自执行函数
方式一:(func)
方式二:(func)() //()就是函数执行
1.
function add(){
var a=b=3 //预解析 a是局部变量,b是全局变量 var a=b;b=3(b会被提升至全局变量)
}
console.log(a) //err
console.log(b) //3

  1. var n=1
    function show(){
    console.log(n)
    n=455
    console.log(n)
    }
    show(n)
    console.log(n)
    输出—
    1
    455
    455

  2. var n=1
    function show(){
    console.log(n)
    function n(){}
    }
    show(n)

输出—
func
只要没有再对n赋值,就是func

  1. var obj1 = {
    a: “hellow”
    }
    var obj2 = obj1
    obj2.a = “world”
    console.log(obj1) //{a: “world”}

  2. function c0() {
    var b = 1

         function a0() {
             console.log(b) //un
             var b = 2
             console.log(b) //2
         }
         a0()
         console.log(b) //1
     }
     c0()
    
<script type="text/javascript">
    var f = true
    if (f === true) {
        var a = 10
    }
    function fn() {
        var b = 20
        c = 30
    }
    fn()
        console.log(a)  //10
        console.log(b) //err
        console.log(c)  //30
</script>

9. 防抖和节流

防抖:事件发生后,n秒只触发一次,若在n秒内再次触发,则重新计算(再等n秒)。
分为两类:
第一类:先延时再执行 【延时可以重新计算】
只要在每次执行事件前,先清除定时器,再开启一个定时器即可

第二类:先执行后延时 【延时可以重新计算】
需要借助一个变量来存储当前的定时器的状态,通过不同的状态,来执行不同的事件
点击吧

节流:事件发生后,n秒只能触发一次。
先设一个定时器句柄(let time)
只写定时器没有的情况,在里面添加一个定时器,并调用相关事件

当无法使用定时器时,使用时间戳(时间间隔),同样可以实现类似效果

获取时间:
function show(){
var time1=new Date()
var time2=Date.now() //比较两次时间的间隔
console.log(“time1—”+time1)
console.log(“time2—”+time2)
}
输出----
time1—Tue Jan 28 2020 14:51:51 GMT+0800 (中国标准时间)
time2—1580194311067

10. 对象拷贝

  1. 浅拷贝,
    是地址引用,可以利用 for in来完成

法一:
var obj = {
name: “1”
}
var obj1 = {}
for (key in obj) {
obj1[key] = obj[key] //不能用.
}
console.log(obj1)

法二:
let Person = {
name: ‘123’
}
var person = {}
Object.assign(person, Person) //浅拷贝
person.name = “111”
console.log(person === Person) //false
console.log(person.name) //111
console.log(Person.name) //123
2.深拷贝
通过递归方式实现深拷贝
有瑕疵
//1.深拷贝
obj = {
name: “tom”,
id: 18,
goods: {
id: 123
},
list: [1, 23, 1, 4]
}

    function deepClone(obj) {
        var target = {};
        for (var key in obj) {
            //JavaScript中Object对象原型上的hasOwnProperty()用来判断一个属性是定义在对象本身而不是继承自原型链。
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                if (typeof obj[key] === 'object') { //对象
                    target[key] = deepClone(obj[key]);
                } else { //数组
                    target[key] = obj[key]; //输入一个数组,最后返回值是对象
                }
            }
        }
        return target;
    }
    console.log(deepClone(obj))
</script>

通过json的方式实现
完美解决
obj = {
name: “tom”,
id: 18,
goods: {
id: 123
},
list: [1, 23, 1, 4]
}

    function deepClone(obj) {
        let result = JSON.parse(JSON.stringify(obj));
        return result;
    }

    console.log(deepClone(obj))
</script>