js面试篇

防抖与节流

防抖和节流都是为了解决短时间内大量触发某函数而导致的**性能问题,**比如触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。

  1. 防抖:在事件被触发n秒后再执行回调函数,如果在这n秒内又被触发,则重新计时。

    使用场景:搜索框输入关键词搜索,监听keyup事件调取接口,就会一直调取接口;

    优化:

    (1) 用户在输入框中连续输入一串字符后,只会在输入完后去执行最后一次的查询ajax请求,这样可以有效减少请求次数,节约请求资源;

    (2) window的resize、scroll事件,不断地调整浏览器的窗口大小、或者滚动时会触发对应事件,防抖让其只触发一次;

    操作:使用setTimeout定时器,设定一个延迟时间200ms

    每一次事件被触发,都会清除当前的 timer 然后重新设置超时调用,即重新计时。 这就会导致如果在200ms内再次触发,就会重新开启定时器,不会执行回调;只有200ms内没有再次触发,倒计时才不会被重置,才会执行一次;

  2. 节流:规定一个单位时间内,只能触发一次事件的回调函数,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

    使用场景:监听他的touchmove事件,touchmove时改变它的位置实现拖拽效果,每100ms或者200ms读一次位置

    let flag = true
    function throttle(){
        if (!flag) return
        flag = false
        setTimeout(function(){
            submit()
            flag = true
        },1000)
    }
    

    实现:定义一个标识为true,当事件触发,将标识修改为false,定时1s之后执行回调并重置flag为true;

    扫描二维码关注公众号,回复: 13259508 查看本文章

    所以若1s内再次触发,flag为false,就return

定时器使用后自己是不会清理的,都需要主动释放一下,timer = null;

如何排查内存泄露

js内存泄漏通常是由于闭包所引起的,Devtool已经提供了检查的工具,这就是内存Memory面板

什么是闭包

闭包是指有权访问另一个函数作用域内变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以 访问到当前函数的局部变量。

闭包有两个常用的用途。

  • 闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,我们可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
  • 函数的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。
function a(){
    
    
    var n = 0;
    function add(){
    
    
       n++;
       console.log(n);
    }
    return add;
}
var a1 = a(); //注意,函数名只是一个标识(指向函数的指针),而()才是执行函数;
a1();    //1
a1();    //2  第二次调用n变量还在内存中

数组去重

  1. Array.from(new Set(arr)) :无法去掉“{}”空对象

  2. […new Set(arr)]

  3. 新建一个空数组,循环原数组,判断结果数组是否存在当前元素,如果不存在就push进数组

    • 判断存在的方法:includes 、array.indexOf ( item ) === -1
  4. sort()先排序,然后for循环比较第n项与n-1项是否相等,如果相等就splice删除当前项

  5. filter过滤数组

    function unique(arr) {
          
          
      return arr.filter((item, index, arr) => {
          
          
        //当前元素在数组中的第一个索引==当前索引值,说明是第一次出现该值,返回该元素;
        //若第二次出现重复的值,则他的索引等于第一次的索引,不等于当前索引,所以就被过滤掉了
        return arr.indexOf(item, 0) === index;
      });
    }
    

面向对象和面向过程

  1. 面向过程:是一种以过程为中心的编程思想。按照步骤划分问题,然后用函数把这些步骤一步一步实现。

  2. 面向对象:是以对象为核心,而对象是程序运行时刻的基本成分。以功能来划分问题,把构成问题的食物分解成各个对象,描叙各个对象在整个解决问题的步骤中的行为。

  3. 对象具有具有属性和方法:例如身高 和 会 写代码;

  4. 面向对象四大特性:抽象、封装、继承、多态。

    1、抽象

    忽略一个主题中与当前目标无关的东西,专注的注意与当前目标有关的方面。抽象包括两个方面,一个数据抽象,而是过程抽象。

    数据抽象 -->表示世界中一类事物的特征,就是对象的属性。比如鸟有翅膀,羽毛等(类的属性)

    过程抽象 -->表示世界中一类事物的行为,就是对象的行为。比如鸟会飞,会叫(类的方法)

    2、封装

    封装是面向对象的特征之一,是对象和类概念的主要特性。封装就是把对象的属性和方法封装起来,对数据的访问只能通过已定义的界面。如私有变量,用set,get方法获取。

    封装保证了模块具有较好的独立性,使得程序维护修改较为容易。对应用程序的修改仅限于类的内部,因而可以将应用程序修改带来的影响减少到最低限度。

    3、继承

    对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),原始类称为新类的基类(父类)。派生类可以从它的父类哪里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。因此可以说,继承为了重用父类代码,同时为实现多态性作准备。

    4、多态

    对同一个方法传入不同的参数得到了不同形态的结果,即多态。例如,方法重写(子类继承父类并重写父类中已有的方法,那你调用该方法就执行子类中的方法,如果没有重写就调用的是基类中的方法。

    面向对象程序设计具有许多优点:

    1、开发时间短,效率高,可靠性高,所开发的程序更强壮。由于面向对象编程的可重用性,可以在应用程序中大量采用成熟的类库,从而缩短了开发时间。

    2、应用程序更易于维护、更新和升级。继承和封装使得应用程序的修改带来的影响更加局部化。

常用的JS对象

  1. Js 中的对象分为3种:自定义对象 、内置对象、 浏览器对象

  2. 内置对象:Math、 Date 、Array、String等

  3. 浏览器API:浏览器对象模型(BOM)、文档对象模型(DOM)

    • DOM : DOM的最小组成单位叫做节点(node)。文档的树形结构(DOM树),就是由各种不同类型的节点组成。 最顶层的节点就是document节点,它代表了整个文档;是文档的根节点。

      查找节点: **querySelector()、querySelectorAll()、 **getElementById()

    • BOM:属性window.document window.location 只读属性 window.localStorage history

      • 方法:window.alert() window.prompt() window.confirm()

      ​ window.console onLoad() 定时器setTimeout()和clearTimeout() setInterval()和clearInterval()

this指向问题

  1. 普通函数 指向window。一般都指向调用者。window为普通函数的调用者,window.fun();

  2. 对象中方法的调用指向该对象。

  3. 构造函数调用指向实例对象,原型对象里面的方法也指向实例对象

  4. 事件绑定函数指向绑定事件对象

  5. 定时器函数和立即执行函数 都指向window

  6. 箭头函数中this执行父作用域中this的指向

  7. 改变this指向: fn中this指向obj1,改变其this指向为obj2

    1.call方法,改变this指向后,可以直接传参,逗号隔开
    2.bind方法和call方法很相似,改变this指向后,可以直接传参,逗号隔开,但是最后要加()自执行
    3.apply方法,改变this指向后,需要以数组的形式传入实参

    obj.fn.call(obj2,'欢欢','可爱的狗');//欢欢是只可爱的狗
    obj.fn.bind(obj2,'尿尿','有趣的猫')();//尿尿是只有趣的猫
    obj.fn.apply(obj2,['哈尼','小机灵鬼']);//哈尼是只小机灵鬼
    

如何复制一个对象?

深复制和浅复制最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用;

深复制将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象,无法拷贝不可枚举的属性,无法拷贝对象的原型链,Symbol类型不可以正确拷贝

浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。

Symbol类型可以正确拷贝

Object.assign({},obj)  //浅拷贝:对象的属性若是引用类型,便会存在问题
var obj2 = {...obj}  //浅拷贝:对象的属性若是引用类型,便会存在问题
JSON.parse(JSON.stringify(obj)) //深拷贝
for (var key in obj) { newObj[key] = obj[key] } //循环对象,递归

如何复制一个数组?

var c=[].concat(a);
var arr2=[...arr];  //浅拷贝:对象的属性若是引用类型,便会存在问题
var arr2=Array.from(arr1); //不会发生引用,一个改变不会引起另一个数组改变,例如push()

如何遍历对象的属性

  1. Object.keys() 自身的可枚举属性

  2. for…in 自身和原型链上的可枚举属性(属性名是Symbol除外)

  3. Object.getOwnPropertyNames() 遍历自身所有属性

    js中哪些属性可枚举,哪些不可枚举?

    1、js基本数据类型***的原型属性***不可枚举,如Object, Array, Number等。

    2、通过Object.defineProperty()方法指定enumeralbe为false的属性不可枚举。

继承/原型链的概念

  1. 继承:

  2. 原型链:构造函数的内部都有一个 prototype 属性值,这个属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当我们使用构造函数新建一个对象后,在这个对象的内部将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型

    image-20210324211757992

当我们访问对象的一个属性或者方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined

数组和字符串的常用方法

1、 indexOf (顺着查字符)lastIndexOf(倒着查字符)查找在字符串或数组是否存在,不存在返回-1,存在返回首次出现索引

        let a = '1234563'
        let b = [1, 2, 3, 4, 5, 6, 3]
       
        console.log(a.indexOf(8));  //-1
        console.log(a.indexOf(3));  //2
        console.log(a.lastIndexOf(3));  //6
        console.log(b.indexOf(3));  //2
        console.log(b.lastIndexOf(3));  //6

1.1、includes(字符)检查字符串和数组是否存在某一项

        let a = '1234563'
        let b = [1, 2, 3, 4, 5, 6, 3]
       
        console.log(a.includes(3));  //true
        console.log(b.includes(3));  //true

2、 截取字符串substr(start,length)、substring(strat,end不包含end)

		let a = '123456'
        console.log(a.substr(2, 3)); //345
        console.log(a.substr(2)); //3456
        console.log(a.substring(2, 3)); //3
        console.log(a.substring(2)); //3456
        console.log(a.slice(2, 3)); //3

2.1 slice(strat,end不包含end) 截取数组和字符串

		let a = '123456'
        let b = [1, 2, 3, 4, 5, 6]
        console.log(a.slice(2, 3)); //3
        console.log(b.slice(2, 3)); //[3]

3、splice(start,length,5) 数组增删改 参数:起始索引,删除个数,添加项

        let b = [0, 1, 2, 3, 4, 5, 6]
     
        console.log(b.splice(3, 2));  //[3,4]
        console.log(b);  //[0, 1, 2, 5, 6]

4、ary.join(’+’);//数组转字符串 str.split(’’); 字符串分割成数组

 		let a = '123456'
        let b = [0, 1, 2, 3, 4, 5, 6]
       
        console.log(a.split(''));  // ["1", "2", "3", "4", "5", "6"]
        console.log(b.join(':'));  // 0:1:2:3:4:5:6

获取url的参数

		var str = 'http://s.weibo.com/weibo/Aralic?topnav=1&wvr=6'
        function getUrl(url) {
    
    
            var arr = url.split('?')[1]
            var obj = {
    
    }
            arr.split('&').forEach(item => {
    
    
                obj[item.split('=')[0]] = item.split('=')[1]
            });
            return obj
        }
        console.log(getUrl(str));

多维数组转为一维数组

		var arr1 = [1, [[4, 5, 6], 2, [[[7, 8, 9]]], 3]];
        var arr2 = arr1.join(',').split(',')
        var arr3 = []
        // arr2.forEach(item => {
    
    
        //     arr3.push(item * 1)
        // })
        var arr3 = arr2.map(item => {
    
    
            return item * 1
        })
        console.log(arr3);   //[1, 4, 5, 6, 2, 7, 8, 9, 3]

将数字转为千分位数 123,456,789.22

 <script>
        function getnum(n) {
    
    
            if (n.indexOf('.') == -1) {
    
    
                let length = n.length;
                var str = ''
                while (length > 3) {
    
    
                    str = ',' + n.substring(length - 3, length) + str
                    length -= 3
                }
                str = n.substring(0, length) + str
            }
            return str
        }
        function getpoint(m) {
    
    
            if (m.indexOf != -1) {
    
    
                var arr = m.split('.');
                return getnum(arr[0]) + "." + arr[1]
            }
        }
        console.log(getnum('1234567'));  //1,234,567
        console.log(getpoint('1234567.33'));  //1,234,567.33
    </script>

数组前后增加删除
ary.push(1);//在数组的末尾增加一项
ary.unshift(3);//在数组的开头增加一项
ary.pop();//在数组的末尾删除一项
ary.shift();//在数组的开头删除一项

是否是数组

  • Array.isArray([1,2]) // true 判断是否是数组

    obj instanceof Array ; //true

		 let b = [0, 1, 2, 3, 4, 5, 6]
    
        console.log(Array.isArray(b));  //true
        console.log(b instanceof Array);  //true

99乘法表

		function aa() {
    
    
            var str = ""
            for (var i = 1; i <= 9; i++) {
    
    
                for (var j = 1; j <= i; j++) {
    
    
                    str += j + '*' + i + '=' + i * j + ' '
                }
                str += '\n'
            }0
            return str
        }
        console.log(aa());

数组排序

	var arr = [1, 10, 100, 48, 30, 20, 15];

        arr.sort((a, b) => {
    
    
            return a - b      
        })
        console.log(arr);    // a-b升序
		 var arr = [1, 10, 100, 48, 30, 20, 15];
        function sort(a) {
    
    
            for (var i = 0; i < a.length - 1; i++) {
    
    
                for (var j = i + 1; j < a.length; j++) {
    
    
                    if (a[i] < a[j]) {
    
    
                        var tem = a[i];
                        a[i] = a[j];
                        a[j] = tem
                    }
                }
            }
            return a
        }
        console.log(sort(arr));

冒泡排序

var arr = [1, 10, 100, 48, 30, 20, 15];
function pop(a) {
    
    
            for (var i = 0; i < a.length - 1; i++) {
    
    
                for (var j = 0; j < a.length; j++) {
    
    
                    if (a[j] < a[j + 1]) {
    
    
                        var tem = a[j]
                        a[j] = a[j + 1]
                        a[j + 1] = tem
                    }
                }
            }
            return a
        }
        pop(arr)
ary.reverse();//颠倒数组
ary.sort(function(a,b){return b-a;}); 数组排序
  • ary.concat(ary1,ary2) //合并两个数组

  • $(this).index() // jQuery中获取数组某一项的索引

  • str.charAt(index); 返回指定索引位置的字符串

  • str.replace(rgExp/substr,replaceText) ;替换字符串里的字符为什么

  • str.match(rgExp); 正则匹配

  • str.concat(str2) ;拼接字符串

  • str.trim(); 去掉字符串首尾的空格

  • toLowerCase(),把字符串转换成小写的。
    toUpperCase(),把字符串转换成大写的。

循环数组的方法
1、常用的普通循环方法
== forEach 里return不生效 ,要使用for循环 ==

 ary.forEach(function(item,i){
    
    
	console.log(item)
 }); 
for(var i = 0; i < arr.length;i++){
    
    
   console.log(arr[i]);
}
for(var i in arr){
    
    
   console.log(arr[i] +'。。。。' + i);
}

2、返回对每一项处理过的数组

 var arr = ["first","second",'third' ,"fourth"];
 var arr2 = arr.map(function(item){
    
    
    return item.toUpperCase();
 })
 console.log(arr2);
 //输出:
 [FIRST,SECOND,THIRD, FOURTH]

3、筛选数组某些项,返回新数组

	var arr = ["first","second",'third' ,"fourth",3,5,8];
    var arr3 = arr.filter(function(item){
    
    
        if(typeof item == 'number'){
    
    
            return item;
        }
    })
    console.log(arr3);
    //输出结果: 
    [3,5,8]      

4、检验数组是否每一项都符合某个条件,返回布尔值

var arr = ["first","second",'third' ,"fourth",3,5,8];
var bol = arr.every(function(element){
    
    
    if(typeof element == 'string'){
    
    
        return element;
    }
 })
 console.log(bol); //false

5、检测数组是否存在符合条件的项,返回布尔值,只要循环到符合条件的就停止,提高性能

	var arr = ["first","second",'third' ,"fourth",3,5,8];
    var bol = arr.some(function(element){
    
    
        if(typeof element == 'string'){
    
    
            return element;
        }
    })
    console.log(bol); //true

6、arr.find():查找出第一个符合条件的数组成员,并返回该成员,如果没有找到就返回undefine

    let arr = [23,40,50]
	let res = arr.find((val,index,arr)=>{
    
    
		return val >30
	})
   console.log(res) //返回:40

7、arr.findIndex() :第一个符合条件的数组成员的位置,找不到返回 -1

    let arr = [23,40,50]
	let res = arr.findIndex((val,index,arr)=>{
    
    
		return val >30
	})
   console.log(res) //返回:1

8、arr.fill(填充的东西,开始位置,结束位置)

arr.fill('默认值'1,2)
console.log(arr)//[empty,'默认值','默认值',empty]

9、arr.includes().数组是都包含某个元素,包含返回true,不包含返回false

字符串的常用方法

  • str.charAt(index); 返回指定索引位置的字符串

  • str.indexOf(searchString,startIndex); 返回子字符串第一次出现的位置
    str.lastIndexOf(searchString,startIndex); 从右往左找子字符串,找不到时返回-1

  • str.substring(start,end); 截取从start到end-1的字符
    str.slice(start,end); 截取从start到end-1的字符
    str.substr(start,length); 截取从start到end的字符
    除了 slice() 和 substr() 方法里的负值是代表从右截取,其他方法里的负值一律作为0处理

  • str.split(’’); 字符串分割成数组

  • str.replace(rgExp/substr,replaceText) ;替换字符串里的字符为什么

  • str.match(rgExp); 正则匹配

  • str.concat(str2) ;拼接字符串

  • str.trim(); 去掉字符串首尾的空格

  • toLowerCase(),把字符串转换成小写的。
    toUpperCase(),把字符串转换成大写的。

export,import ,export default的区别

ES6模块主要有两个功能:export和import

  • export用于对外输出本模块(一个文件可以理解为一个模块)变量的接口
  • import用于在一个模块中加载另一个含有export接口的模块。
  • export与export default均可用于导出常量、函数、文件、模块等
  • 在一个文件或模块中,export、import可以有多个,export default仅有一个
  • 通过export方式导出,在导入时要加{ },export default则不需要
export var name="李四";
import { name } from "/.a.js" //路径根据你的实际情况填写

export default name
import name from "/.a.js" 这里name不需要大括号

1、js数据类型

  1. 五种基本数据类型:Number、String、Boolean、null、undefined
  2. 一个引用数据类型:Object(包含Array、Function、Date等)
  3. es6新增一个基本数据类型Symbol(标识独一无二的值,常用作定义对象的唯一属性名,或者定义常量)
// 相同参数 Symbol() 返回的值不相等
let sy = Symbol("KK");
console.log(sy);   // Symbol(KK)
typeof(sy);        // "symbol"

let sy1 = Symbol("kk"); 
sy === sy1;       // false
//应用
let sy = Symbol("key1");
 
// 写法1
let syObject = {
    
    };
syObject[sy] = "kk";
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 写法2
let syObject = {
    
    
  [sy]: "kk"
};
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 写法3
let syObject = {
    
    };
Object.defineProperty(syObject, sy, {
    
    value: "kk"});
console.log(syObject);   // {Symbol(key1): "kk"}
  1. es10新增一个基本数据类型BigInt (用于当整数值大于Number数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高分辨率的时间戳,使用大整数id)
console.log(10n === 10);    // → false
console.log(typeof 10n);    // → bigint
console.log(typeof 10);     // → number

BigInt("9007199254740995");    // → 9007199254740995n

比较

  • 原始数据类型:直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
  • 引用数据类型:同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

数据类型的转换

  • 转换为布尔值(调用Boolean()方法)
  • 转换为数字(调用Number()、parseInt()和parseFloat()方法)
  • 转换为字符串(调用.toString()或者String()方法)
null===null
true
NaN===NaN
false
undefined==undefined
true

数据类型的判断

  • typeof :除了null被解析为Object,其他数据类型均正常
console.log(typeof 2);               // number
console.log(typeof true);            // boolean
console.log(typeof 'str');           // string
console.log(typeof []);              // object     []数组的数据类型在 typeof 中被解释为 object
console.log(typeof function(){
    
    });    // function
console.log(typeof {
    
    });              // object
console.log(typeof undefined);       // undefined
console.log(typeof null);            // object     null 的数据类型被 typeof 解释为 object
  • instanceof:nstanceof判断引用数据类型Object(Array,Function,Object),而基本数据类型不行

Javascript 的作用域和作用域链

作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。

  • 全局作用域:在代码中任何地方都能访问到的对象
  • 局部作用域:局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部

作用域链:

  • 作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,我们可以访问到外层环境的变量和 函数。
  • 作用域链的本质上是一个指向变量对象的指针列表。变量对象是一个包含了执行环境中所有变量和函数的对象。作用域链的前 端始终都是当前执行上下文的变量对象。全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最后一个对象。
  • 从作用域链的结构可以看出,在运行期上下文的作用域链中,标识符所在的位置越深,读写速度就会越慢。如上图所示,因为全局变量总是存在于运行期上下文作用域链的最末端,因此在标识符解析的时候,查找全局变量是最慢的。所以,在编写代码的时候应尽量少使用全局变量,尽可能使用局部变量。一个好的经验法则是:如果一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使用。

JavaScript 原型,原型链? 有什么特点?

构造函数的内部都有一个 prototype 属性值,这个属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当我们使用构造函数新建一个对象后,在这个对象的内部将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。一般来说我们是不应该能够获取到这个值的,但是现在浏览器中都实现了 proto 属性来让我们访问这个属性,但是我们最好不要使用这个属性,因为它不是规范中规定的。ES5 中新增了一个 Object.getPrototypeOf() 方法,我们可以通过这个方法来获取对象的原型。当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是 Object.prototype 所以这就是我们新建的对象为什么能够使用 toString() 等方法的原因。
特点:
JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。

什么是闭包

闭包是指有权访问另一个函数作用域内变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以 访问到当前函数的局部变量。

闭包有两个常用的用途。

  • 闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,我们可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
  • 函数的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。
function a(){
    
    
    var n = 0;
    function add(){
    
    
       n++;
       console.log(n);
    }
    return add;
}
var a1 = a(); //注意,函数名只是一个标识(指向函数的指针),而()才是执行函数;
a1();    //1
a1();    //2  第二次调用n变量还在内存中

垃圾回收机制

  1. JavaScript 中的内存管理是自动执行的,而且是不可见的。我们创建基本类型、对象、函数……所有这些都需要内存。

事件委托是什么?

事件委托 本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到
目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理。
使用事件代理我们可以不必要为每一个子元素都绑定一个监听事件,这样减少了内存上的消耗。并且使用事件代理我们还可以实现事件的动态绑定,比如说新增了一个子节点,我们并不需要单独地为它添加一个监听事件,它所发生的事件会交给父元素中的监听函数来处理。

数组和字符串有哪些原生方法

在这里插入图片描述
在这里插入图片描述

说说CSS选择器以及这些选择器的优先级

!important
内联样式(1000)
ID选择器(0100)
类选择器/属性选择器/伪类选择器(0010)
元素选择器/关系选择器/伪元素选择器(0001)
通配符选择器(0000)

如何理解闭包

定义和用法: 一个父函数里面包含了一个子函数,子函数调用了父函数内部的变量,如果子函数在外部被调用,就产生了闭包。简单的说,闭包就是能够读取其他函数内部变量的函数。
闭包的作用: ①读取其他函数内部的变量 ②变量保存在内存中
注意: 使用过多的闭包会消耗大量内存,造成网页的性能问题,可以在函数执行完成之前把不需要的局部变量删除。

请指出document load和document ready的区别?

ready:页面的文档结构加载完成,不包括图片视频等非文字内容。 load:所有页面元素都加载完成 ready的速度比load快

对浏览器内核的理解

主要分为渲染引擎和js引擎 渲染引擎:主要负责取得网页的(html,xml,图片等),整理信息结合css渲染页面,计算网页的显示方式,浏览器内核的不同对网页的语法解释也会有所不同,所以渲染效果也会有所不同,这也是我们需要做兼容性处理的原因。
js引擎:解析和执行js来达到网页的动态交互效果。
最开始渲染引擎和js引擎没有太区分,后来越来越独立化,然后内核一般就是指渲染引擎了。

HTTP请求报文组成

HTTP请求报文主要分成3个部分:请求行,请求头,请求体,它们从上到下分布

请求行
请求行是一行,主要格式为
[请求方法] [请求URL] HTTP协议及版本

请求方法
常用的请求方法有GET和POST,此外还有h5新特性、HEAD、OPTIONS、PUT、TRACE等等

CSS3 新特性

选择器
背景和边框
文本效果
2D/3D 转换
动画、过渡
多列布局
用户界面
选择器
:last-child /* 选择元素最后一个孩子 /
:first-child /
选择元素第一个孩子 /
:nth-child(1) /
按照第几个孩子给它设置样式 /
:nth-child(even) /
按照偶数 /
:nth-child(odd) /
按照奇数 /
:disabled /
选择每个禁用的E元素 /
:checked /
选择每个被选中的E元素 /
:not(selector) /
选择非 selector 元素的每个元素 /
::selection /
选择被用户选取的元素部分 */
伪类和伪元素:

根本区别在于它们是否创造了新的元素(抽象)

伪类:用于向某些选择器添加特殊的效果(没有创建新元素)
复制代码
:last-child /* 选择元素最后一个孩子 /
:first-child /
选择元素第一个孩子 /
:nth-child(1) /
按照第几个孩子给它设置样式 /
a:link {color: #FF0000} /
未访问的链接 /
a:visited {color: #00FF00} /
已访问的链接 /
a:hover {color: #FF00FF} /
鼠标移动到链接上 /
a:active {color: #0000FF} /
选定的链接 */
复制代码

伪元素:创建了 html 中不存在的元素,用于将特殊的效果添加到某些选择器
::before {} /* 选择器在被选元素的前面插入内容和定义css,使用 content 属性来指定要插入的内容。 /
::after {} /
选择器在被选元素的后面插入内容和定义css,使用 content 属性来指定要插入的内容。 /
:first-letter /
选择该元素内容的首字母 /
:first-line /
选择该元素内容的首行 /
::selection /
选择被用户选取的元素部分 */

背景和边框
背景:
background-size:规定背景图片的尺寸(cover:填充;100% 100%:拉伸)
background-origin:规定背景图片的定位区域
对于 background-origin 属性,有如下属性
背景图片可以放置于 content-box、padding-box 或 border-box 区域

边框:
border-radius:圆角
box-shadow / text-shadow:阴影
border-image:边框图片
文本效果
属性 描述

text-shadow 向文本添加阴影
text-justify 规定当 text-align 设置为 “justify” 时所使用的对齐方法
text-emphasis 向元素的文本应用重点标记以及重点标记的前景色
text-outline 规定文本的轮廓
text-overflow 规定当文本溢出包含元素时发生的事情
text-wrap 规定文本的换行规则
word-break 规定非中日韩文本的换行规则
word-wrap 允许对长的不可分割的单词进行分割并换行到下一行
text-decoration 文本修饰符:overline、line-through、underline 分别是上划线、中划线、下划线
@font-face 自定义字体
渐变,CSS3新增了渐变效果,包括 linear-gradient(线性渐变)和 radial-gradient(径向渐变)
详看:https://www.ainyi.com/krry_project
2D/3D 转换
2D 转换(transform)
translate():元素从其当前位置移动,根据给定的 left(x 坐标) 和 top(y 坐标) 位置参数。 transform: translate(50px, 100px);
rotate():元素顺时针旋转给定的角度。若为负值,元素将逆时针旋转。transform: rotate(30deg);
scale():元素的尺寸会增加或减少,根据给定的宽度(X 轴)和高度(Y 轴)参数,也可以一个值(宽高)。transform: scale(2,4);
skew():元素翻转给定的角度,根据给定的水平线(X 轴)和垂直线(Y 轴)参数。transform: skew(30deg, 20deg);
matrix(): 把所有 2D 转换方法组合在一起,需要六个参数,包含数学函数,允许您:旋转、缩放、移动以及倾斜元素。transform:matrix(0.866,0.5,-0.5,0.866,0,0);

3D 转换
rotateX():元素围绕其 X 轴以给定的度数进行旋转。transform: rotateX(120deg);
rotateY():元素围绕其 Y 轴以给定的度数进行旋转。transform: rotateY(130deg);
perspective:规定 3D 元素的透视效果
动画、过渡
过渡效果(transition),使页面变化更平滑,以下参数可直接写在 transition 后面
transition-property :执行动画对应的属性,例如 color,background 等,可以使用 all 来指定所有的属性。
transition-duration:过渡动画的一个持续时间。
transition-timing-function:在延续时间段,动画变化的速率,常见的有:ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier
transition-delay:延迟多久后开始动画

## 动画(animation)

先定义 @keyframes 规则(0%,100% | from,to)
然后定义 animation,以下参数可直接写在 animation 后面
animation-name: 定义动画名称
animation-duration: 指定元素播放动画所持续的时间长
animation-timing-function: ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier(, , , ): 指元素根据时间的推进来改变属性值的变换速率,即动画的播放方式
animation-delay: 指定元素动画开始时间
animation-iteration-count: infinite | number:指定元素播放动画的循环次数
animation-direction: normal | alternate: 指定元素动画播放的方向,其只有两个值,默认值为normal,如果设置为 normal 时,动画的每次循环都是向前播放;另一个值是 alternate,规定动画在下一周期逆向地播放(来去播放)
animation-play-state: running | paused :控制元素动画的播放状态
多列布局
通过CSS3,能够创建多个列来对文本进行布局

column-count: 规定元素应该被分隔的列数
column-gap: 规定列之间的间隔
column-rule: 设置列之间的宽度、样式和颜色规则
用户界面
CSS3中,新的用户界面特性包括重设元素尺寸、盒尺寸以及轮廓等

resize
box-sizing
outline-offset
resize 属性规定是否可由用户调整元素尺寸。如果希望此属性生效,需要设置元素的 overflow 属性,值可以是 auto、hidden 或 scroll

div {
resize: both; /* none|both|horizontal|vertical; */
overflow: auto;
}

box-sizing 属性可设置的值有 content-box、border-box 和 inherit
content-box 是W3C的标准盒模型,元素宽度 = 内容宽度 + padding + border:意思是 padding 和 border 会增加元素的宽度,以至于实际上的 width 大于原始设定的 width
border-box 是ie的怪异盒模型,元素宽度 = 设定的宽度,已经将 padding 和 border 包括进去了,比如有时候在元素基础上添加内距 padding 或 border 会将布局撑破,但是使用 border-box 就可以轻松完成
inherit:规定应从父元素继承 box-sizing 属性的值

outline-offset 属性对轮廓进行偏移,并在超出边框边缘的位置绘制轮廓

CSS 兼容内核
-moz-:代表FireFox浏览器私有属性
-ms-:代表IE浏览器私有属性
-webkit-:代表safari、chrome浏览器私有属性
-o-:代表opera浏览器私有属性

猜你喜欢

转载自blog.csdn.net/weixin_43848576/article/details/115674991