JavaScript前端面试

40 篇文章 1 订阅

js数据类型

动态类型、弱类型语言

  • JavaScript 是一种有着动态类型的动态语言。变量的类型根据赋值的类型而变;
  • JavaScript 也是一个弱类型语言。当操作不匹配的类型会隐式类型转换,而不是抛出一个错误。
let foo = 42; // foo 现在是一个数值
foo = "bar"; // foo 现在是一个字符串
foo = true; // foo 现在是一个布尔值

const foo = 42; // foo is a number
const result = foo + "1"; // 类型不一致时,强制类型转换
console.log(result); // 421

所有数据类型

  • 五种基本数据类型:Number、String、Boolean、null、undefined
  • es6新增一个基本数据类型Symbol(标识独一无二的值,常用作定义对象的唯一属性名,或者定义常量)
  • es10新增一个基本数据类型BigInt (用于当整数值大于Number数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高分辨率的时间戳,使用大整数id)
  • 一个引用数据类型:Object(包含Array、Function、Date等)

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"}

BigInt

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

数据类型的判断

  • 除了 null,所有原始类型都可以使用 typeof 运算符测试。typeof null 返回 “object”,因此必须使用 null == null。
  • undefined 表示没有任何值,null 表示没有任何对象
  • NaN(“Not a Number”)是一个特殊种类的数值,算术运算的结果不表示数值。它也是 JavaScript 中唯一不等于自身的值
    在这里插入图片描述
console.log(typeof {});              // object
console.log(typeof []);              // object    []数组的数据类型在 typeof 中被解释为 object
console.log(typeof function(){});    // function
console.log(typeof null);            // object     null 的数据类型被 typeof 解释为 object

null == null          // true
null === null        // true
undefined == undefined    // true
undefined === undefined   // true
  • instanceof:instanceof判断引用数据类型Object(Array,Function,Object),而基本数据类型不行

数组和字符串的常用方法

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

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

截取字符串

  • substr(start,length)
  • substring(strat,end不包含end)
  • slice(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

截取数组

  • slice(strat,end不包含end) 截取数组和字符串
  • splice(start,length,5) 数组增删改 参数:起始索引,删除个数,添加项
		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]
        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]

join数组转字符串 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(arr) // true 判断是否是数组
arr 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());

数组排序sort

	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)

字符串方法

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(),把字符串转换成大写的。

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

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

循环数组的方法

普通循环方法

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);
 }

map

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

filter

	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]      

every

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

some

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

	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

find

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

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

findIndex

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

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

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

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

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不需要大括号

Javascript 的作用域和作用域链

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

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

作用域链:

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

JavaScript 继承的几种实现方式

我了解的 js 中实现继承的几种方式有:

(1)第一种是以原型链的方式来实现继承,但是这种实现方式存在的缺点是,在包含有引用类型的数据时,会被所有的实例对象所共享,容易造成修改的混乱。还有就是在创建子类型的时候不能向超类型传递参数。

(2)第二种方式是使用借用构造函数的方式,这种方式是通过在子类型的函数中调用超类型的构造函数来实现的,这一种方法解决了不能向超类型传递参数的缺点,但是它存在的一个问题就是无法实现函数方法的复用,并且超类型原型定义的方法子类型也没有办法访问到。

(3)第三种方式是组合继承,组合继承是将原型链和借用构造函数组合起来使用的一种方式。通过借用构造函数的方式来实现类型的属性的继承,通过将子类型的原型设置为超类型的实例来实现方法的继承。这种方式解决了上面的两种模式单独使用时的问题,但是由于我们是以超类型的实例来作为子类型的原型,所以调用了两次超类的构造函数,造成了子类型的原型中多了很多不必要的属性。

(4)第四种方式是原型式继承,原型式继承的主要思路就是基于已有的对象来创建新的对象,实现的原理是,向函数中传入一个对象,然后返回一个以这个对象为原型的对象。这种继承的思路主要不是为了实现创造一种新的类型,只是对某个对象实现一种简单继承,ES5 中定义的 Object.create() 方法就是原型式继承的实现。缺点与原型链方式相同。

(5)第五种方式是寄生式继承,寄生式继承的思路是创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本,然后对象进行扩展,最后返回这个对象。这个扩展的过程就可以理解是一种继承。这种继承的优点就是对一个简单对象实现继承,如果这个对象不是我们的自定义类型时。缺点是没有办法实现函数的复用。

(6)第六种方式是寄生式组合继承,组合继承的缺点就是使用超类型的实例做为子类型的原型,导致添加了不必要的原型属性。寄生式组合继承的方式是使用超类型的原型的副本来作为子类型的原型,这样就避免了创建不必要的属性。

谈谈你对this、call、apply和bind的理解

  1. 在浏览器里,在全局范围内this 指向window对象;
  2. 在函数中,this永远指向最后调用他的那个对象;
  3. 构造函数中,this指向new出来的那个新的对象;
  4. call、apply、bind中的this被强绑定在指定的那个对象上;
  5. 箭头函数中this比较特殊,箭头函数this为父作用域的this,不是调用时的this.要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来;
  6. apply、call、bind都是js给函数内置的一些在 js 中我们是使用构造函数来新建一个对象的,每一个

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变量还在内存中

事件委托是什么?

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

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

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

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

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

简述从浏览器地址栏输入url到显示页面的步骤

涉及的主要流程或步骤有: ①浏览器根据请求的URL,交给DNS域名解析,找到真实的ip,交给域名解析。 ②服务器交给后端处理完成后返回的数据,浏览器接收文件HTML,CSS,JS图片等。 ③浏览器对加载的资源进行语法解析,建立相应的数据内部结构。 ④解析html,创建dom树,自上而下的顺序 ⑤解析css,优先级:浏览器默认设置<用户设置<外部样式<内联样式<HTML中的style样式; ⑥将css与dom合并,构建渲染树 ⑦布局重绘重排,页面完成渲染。

sessionStorge , localStorge , cookie , Web Storage之间的区别

①数据存储大小 cookie:4kb webStorge:5mb
②数据存储有效期限 cookie:根据自己的设置时间 sessionStorage:关闭窗口后失效
localStorage:永久有效除非js删除或者浏览器删除 ③作用域 cookie和localStorage是在同源窗口,同一个浏览器共享的,sessionStorage只在同一个标签页共享。

网站性能优化

网站性能优化主要从以下几个方面进行优化: ①资源文件js css 图片合并压缩 ②减少页面dom操作,操作多的话可以考虑使用虚拟dom ③减少http请求 ④使用cdn加速 cdn的作用:cdn可以处理整个网站 70%-95%的访问量,从而解决网站的并发量,简单的说就是通过在不同地点缓存内容,然后通过负载平衡等技术将用户请求定向到最近的缓存服务器上获取内容,提高用户访问网站的响应速度。 ⑤减少cookie大小

对浏览器内核的理解

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

HTTP请求报文组成

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

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

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

h5新特性

1.语义化标签:

header,nav,main,article,aside,footer
音频视频:audio、video 、canvas

b.引入第三方插件:html5.shiv.min.js,引入后可以做到兼容IE8:

2.表单类型

.新增type属性,之前的是text,password,新增有email,tel,url,number,search,range,color,time,date,datetime-local,month,week

email提供验证,包含@+域名;

tel没有验证,在移动端可以打开数字键盘;

url验证,必须包含http://

number,只能输入数字(可小数),有上调下降箭头调整,可以设置max,min,value默认值

search,提供人性化体验,最后有个x全部删除

range,设置范围max,min拉动条

color,颜色选取

time,点击选取–:–

date,箭头下拉有日历控件选择 年/月/日

datetime,大多数浏览器不支持,只有safari支持。

datetime-local:浏览器支持

month,week,生成月,周

3.表单其他属性

placeholder:提示文本,提示占位,“请输入xxx”

autofocus:自动获取焦点,不用鼠标去点
required:必须输入

pattern:正则表达式验证,

autocomplete:自动补全,但是有两个前提:

(1)必须成功提交过

(2)加autocomplete的这个元素必须有name属性

用户名:
回顾:

*:任意个

?:0个或1个

+:1个或多个

手机号码:pattern=“^(+86)?1\d{10}$”

手机:

multiple:

(a) type为file:上传文件,加multiple可以上传多个文件

(b) type为email:多邮箱之间用逗号分割,加multiple可实现

form:指定表单id,不在表单内,但是提交也可以随着该表单的提交一起提交。
4、本地存储:
localStorage - 没有时间限制的数据存储;
sessionStorage - 针对一个 session 的数据存储,当用户关闭浏览器窗口后,数据会被删除

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浏览器私有属性

防抖与节流

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

  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;

    所以若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. JavaScript 中的内存管理是自动执行的,而且是不可见的。我们创建基本类型、对象、函数……所有这些都需要内存。

事件委托是什么?

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小曲曲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值