【JavaScript】Interview必背(详细版)
作者:互联网
- 本文在巨人的肩膀上进行了整理总结和修改。
文章目录
- 1.介绍一下js的数据类型有哪些,值是如何存储的
- 2. && 、 || 和 !! 运算符分别能做什么
- 3.数据类型转换
- 4.数据类型判断 typeof,instanceof,constructor,Object.prototype.toString.call()
- 5. 介绍 js 有哪些内置对象?
- 6.undefined与undeclared的区别
- 7.null和undefined的区别
- 8.{}和[]的valueOf和toString的结果是什么
- 9.JS的作用域和作用域链
- 10.JS创建对象的几种方式
- 11.JS继承的几种实现方式
- 12.call和apply的用法(详细介绍)
- 13.原型与原型链
- 14.什么是闭包,为什么要用它?
- 15.了解DOM和BOM吗?
- 16.三种事件模型是什么?
- 17.事件委托是什么?
- 18.事件传播是什么?
- 19.事件捕获是什么?
- 20.事件冒泡是什么?
- 21.DOM操作 添加移除移动复制创建和查找结点
- 22.js数组和字符串原生方法
- 23.常用正则表达式
- 24.Ajax 是什么? 如何创建一个 Ajax?
- 25. js 延迟加载的方式有哪些?
- 26.谈谈你对模块化开发的理解?
- 27.js 的几种模块规范?模块加载方案?
- 28.AMD 和 CMD 规范的区别?
- 29.ES6 模块与 CommonJS 模块、AMD、CMD 的差异。
- 30. requireJS的核心原理是什么?
1.介绍一下js的数据类型有哪些,值是如何存储的
- JavaScript一共有8种数据类型,其中有7种基本数据类型:
Undefined、Null、Boolean、Number、String、Symbol(es6新增,表示独一无二的值)和BigInt(es10新增)
;
1种引用数据类型——Object(Object本质上是由一组无序的名值对组成的)。里面包含 Dunction、Array、Date等。
JavaScript不支持任何创建自定义类型的机制,而所有值最终都将是上述 8 种数据类型之一。 - 原始数据类型:直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
- 引用数据类型:同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。引用数据类型在
栈中存储了指针,该指针指向堆中该实体的起始地址
。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
2. && 、 || 和 !! 运算符分别能做什么
- && 叫逻辑与,在其操作数中找到第一个虚值表达式并返回它,如果没有找到任何虚值表达式,则返回最后一个真值表达式。它采用短路来防止不必要的工作。
- || 叫逻辑或,在其操作数中找到第一个真值表达式并返回它。这也使用了短路来防止不必要的工作。在支持 ES6 默认函数参数之前,它用于初始化函数中的默认参数值。
- !! 运算符可以将右侧的值强制转换为布尔值,这也是将值转换为布尔值的一种简单方法。、、
3.数据类型转换
只有三种情况,分别是
- 转换为布尔值(Boolean())
- 转换为数字(Number()、parseInt()、parseFloat())
- 转换为字符串(.toString()或者String()方法)
null和underfined没有.toString方法
4.数据类型判断 typeof,instanceof,constructor,Object.prototype.toString.call()
- typeof
typeof 对于原始类型来说,除了 null 都可以显示正确的类型
被解释为object类型的还有数组和null
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
typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型,所以想判断一个对象的正确类型,这时候可以考虑使用 instanceof
- instanceof
instanceof可以正确判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的prototype
console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
// console.log(undefined instanceof Undefined);
// console.log(null instanceof Null);
可以看出instanceof可以精确判断出一个引用数据类型的类型,但是不能精确判断出基本数据类型
因为instanceof是用来测试一个对象在该原型链中是否存在构造函数的prototype属性,也就是判断该对象是否是某一数据类型的实例。
而数字类型,字符串类型,布尔类型不是实例,所以为false。
- constructor
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
这也是通过判断构造函数中的prototype来进行的
但是修改prototype过后就会使constructor返回值修改,即使他的原型是本来的,但是原型的prototype改了。
function Fn(){};
Fn.prototype=new Array();
var f=new Fn();
console.log(f.constructor===Fn); // false
console.log(f.constructor===Array); // true
- Object.prototype.toString.call()
var a = Object.prototype.toString;
console.log(a.call(2));
console.log(a.call(true));
console.log(a.call('str'));
console.log(a.call([]));
console.log(a.call(function(){}));
console.log(a.call({}));
console.log(a.call(undefined));
console.log(a.call(null));
用call()对传进去的值进行该值的原型链prototype的构造函数type转换为字符串过后的回调
5. 介绍 js 有哪些内置对象?
可以理解成有哪些内核定义的对象
js 中的内置对象主要指的是在程序执行前存在全局作用域里的由 js定义的一些全局值属性、函数和用来实例化其他对象的构造函数对象。一般我们经常用到的如全局变量值 NaN、undefined,全局函数如 parseInt()、parseFloat() 用来实例化对象的构造函数如 Date、Object 等,还有提供数学计算的单体内置对象如 Math 对象。
全局的对象( global objects )或称标准内置对象,不要和 “全局对象(global object)” 混淆。这里说的全局的对象是说在
全局作用域里的对象。全局作用域中的其他对象可以由用户的脚本创建或由宿主程序提供。
标准内置对象的分类
(1)值属性,这些全局属性返回一个简单值,这些值没有自己的属性和方法。
例如 Infinity、NaN、undefined、null 字面量
(2)函数属性,全局函数可以直接调用,不需要在调用时指定所属对象,执行结束后会将结果直接返回给调用者。
例如 eval()、parseFloat()、parseInt() 等
(3)基本对象,基本对象是定义或使用其他对象的基础。基本对象包括一般对象、函数对象和错误对象。
例如 Object、Function、Boolean、Symbol、Error 等
(4)数字和日期对象,用来表示数字、日期和执行数学计算的对象。
例如 Number、Math、Date
(5)字符串,用来表示和操作字符串的对象。
例如 String、RegExp
(6)可索引的集合对象,这些对象表示按照索引值来排序的数据集合,包括数组和类型数组,以及类数组结构的对象。例如 Array
(7)使用键的集合对象,这些集合对象在存储数据时会使用到键,支持按照插入顺序来迭代元素。
例如 Map、Set、WeakMap、WeakSet
(8)矢量集合,SIMD 矢量集合中的数据会被组织为一个数据序列。
例如 SIMD 等
(9)结构化数据,这些对象用来表示和操作结构化的缓冲区数据,或使用 JSON 编码的数据。
例如 JSON 等
(10)控制抽象对象
例如 Promise、Generator 等
(11)反射
例如 Reflect、Proxy
(12)国际化,为了支持多语言处理而加入 ECMAScript 的对象。
例如 Intl、Intl.Collator 等
(13)WebAssembly
(14)其他
例如 arguments
6.undefined与undeclared的区别
- 已在作用域声明但还没有赋值的变量,是undefined。相反,还没有在作用域中声明的变量是undeclared。
- undeclared的变量引用浏览器会报错。但是我们可以用typeof(…)避免,因为返回的是undefined
7.null和undefined的区别
- 都是基本数据类型,都只有一个值
- undefined是未定义,null代表空对象,主要进行可能会返回对象的变量赋值初始化。
- typeof判断NULL时候返回的是“object”
- 双等号对两个类型的值进行比较会返回true,但是三等号会返回false
8.{}和[]的valueOf和toString的结果是什么
- {} 的 valueOf 结果为 {} ,toString 的结果为 “[object Object]”
- [] 的 valueOf 结果为 [] ,toString 的结果为 “”
9.JS的作用域和作用域链
- 作用域:作用域是定义变量的区域,它有一套访问变量的规则,这套规则来管理浏览器引擎如何在当前作用域以及嵌套的作用域中根据变量(标识符)进行变量查找
- 作用域链:作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,我们可以访问到外层环境的变量和函数。
作用域链本质是指向变量对象的指针列表。
作用域链的前端始终都是当前执行上下文的变量对象,全局对象始终都是作用域链的最后一个对象
查找变量的时候,如果当前没找到,可以沿着作用域链往后查找
创建过程与执行上下文的建立有关
10.JS创建对象的几种方式
- Object构造函数创建
var Person = new Object();
Person.name = 'Nike';
Person.age = 29;
Person指向用new创建Object引用类型的一个新实例,然后进行值的添加
- 使用对象字面量表示
var Person = {};//相当于var Person = new Object();
Person = {
name:'Nike';
age:29;
}
- 工厂模式:
用函数封装对象细节,通过调用函数进行复用。创建出来的对象无法和某个类型联系起来,没有建立对象和类型间的关系。
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson('Nike',29,'teacher');
var person2 = createPerson('Arvin',20,'student');
- 构造函数模式:
只要一个函数通过new调用,就可以称为构造函数。
首先会创建一个对象,然后将对象的原型指向构造函数的prototype,将执行上下文的this值向这个对象,然后用this给对象赋值。
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person('Nike',29,'teacher');
var person2 = new Person('Arvin',20,'student');
优点是:
1.创建的对象和构造函数建立了关系,就是prototype,因此可以通过原型来识别对象的类型;也就是说可以通过该对象的原型的prototype判断类型:
2.无显示创建对象
3.直接将属性和方法赋值给了this对象
4.无return
alert(person1 instanceof Object);//ture
alert(person1 instanceof Person);//ture
//instanceof还可以换成
//1.person1.constructor === Object;
//var a = Object.prototype.toString; a.call(person1) === Person
缺点是:每个方法都要在每个实例上重新创建一遍,方法指的就是我们在对象里面定义的函数。如果方法的数量很多,就会占用很多不必要的内存。
- 原型创建对象模式:
function Person(){}
Person.prototype.name = 'Nike';
Person.prototype.age = 20;
Person.prototype.jbo = 'teacher';
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name ='Greg';
alert(person1.name); //'Greg' --来自实例
alert(person2.name); //'Nike' --来自原型
- 组合使用构造函数模式和原型模式
function Person(name,age,job){
this.name =name;
this.age = age;
this.job = job;
}
Person.prototype = {
constructor:Person,
sayName: function(){
alert(this.name);
};
}
var person1 = new Person('Nike',20,'teacher');
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性
11.JS继承的几种实现方式
由于JS并不是真正面向对象的语言,是基于对象的,没有类的概念,所以继承就要用JS对象的原型prototype机制或者用apply和call方法实现。
可以把prototype看作一个模板,新创建的对象都是这个模板的拷贝(新实例化的对象内部有个_Proto_指针,指向原型对象)
- 继承的第一种方式:对象冒充
通过this.某某函数()将parent函数引入,然后对其进行传参操作,此时该子对象this已经有parent的值了,也就是说一个传参后的副本,销毁最开始的parent引入,就只剩this自己的继承属性了
function Parent(username){
this.username = username;
this.hello = function(){
console.log('hello ' + this.username);
}
}
Parent.prototype.sayMorning = function(){
console.log('good morning ' + this.username);
}
function Child(username,password){
//通过以下3行实现将Parent的属性和方法追加到Child中,从而实现继承
//第一步:this.method是作为一个临时的属性,并且指向Parent所指向的对象,
//第二步:执行this.method方法,即执行Parent所指向的对象函数
//第三步:销毁this.method属性,即此时Child就已经拥有了Parent的所有属性和方法
this.method = Parent;
this.method(username);//最关键的一行
delete this.method;
this.password = password;
this.world = function(){
console.log(this.password);
}
}
var parent = new Parent("zhangsan");
var child = new Child("lisi","123456");
parent.hello();
parent.sayMorning();
child.hello();
child.world();
- 继承的第二种方式:call()方法方式
因为call函数能够回调全局对象的函数/全局函数将该函数赋值给传进去的this上,所以call返回的this对象就是进行父函数回调处理过后的对象,就能够实现继承。
但是this的原型还是child的原型!!!因为传进去的this本身就是child
function Parent(username){
this.username = username;
this.hello = function(){
console.log(this.username);
}
}
Parent.prototype.sayMorning = function(){
console.log('good morning ' + this.username);
}
function Child(username,password){
Parent.call(this,username);
this.password = password;
this.world = function(){
console.log(this.password);
}
}
var parent = new Parent("zhangsan");
var child = new Child("lisi","123456");
parent.hello();
parent.sayMorning();
child.hello();
child.world();
// child.sayMorning(); 通过prototype 添加的方法和属性,不能用来继承
- 继承的第三种方式:apply()方法方式
原理跟call一样,只是传进去的值第一个是this,第二个必须是有内存分配(不是野指针)的数组;例如new Array()或者 var []
function Parent(username){
this.username = username;
this.hello = function(){
console.log(this.username);
}
}
Parent.prototype.sayMorning = function(){
console.log('good morning ' + this.username);
}
function Child(username,password){
Parent.apply(this,new Array(username));
this.password = password;
this.world = function(){
console.log(this.password);
}
}
var parent = new Parent("zhangsan");
var child = new Child("lisi","123456");
parent.hello();
parent.sayMorning();
child.hello();
child.world();
// child.sayMorning(); 通过prototype 添加的方法和属性,不能用来继承
- 继承的第四种方式:原型链方式
原型链方式,即子类通过prototype将所有在父类中通过prototype追加的属性和方法都追加到child里面,从而实现继承。
function Person(){
}
Person.prototype.hello = "hello";
Person.prototype.sayHello = function(){
console.log(this.hello);
}
function Child(){
}
Child.prototype = new Person();//这行的作用是:将Parent中将所有通过prototype追加的属性和方法都追加到Child,从而实现了继承
console.log("判断Child的原型链是不是Person原型的一个实例化对象 " + (Child.prototype.__proto__ === Person.prototype))
//判断Child的原型链是不是Person原型的一个实例化对象 true
Child.prototype.world = "world";
Child.prototype.sayWorld = function(){
console.log(this.world);
}
var c = new Child();
c.sayHello();
c.sayWorld();//undefined
缺点1:通过prototype 添加的方法和属性,不能用来继承!!!!为什么呢??下面有解释
- 这里对
Child.prototype = new Person();
进行解释:
- 这里分开分析:
创建一个Person原型对象实例
var obj = new Person();
Child对象的原型 指向 这个Person原型对象的实例
Child.prototype = obj;- 也就是说
Child对象的原型其实是Person原型的实例化对象
Child.prototype.__proto__ === Person.prototype;
因为子对象的原型是父对象的原型的实例化对象,所以不能继承父对象的方法和属性。
- 继承的第五种方式:混合方式, 混合了call或者apply方式、原型链方式
function Parent(hello){
this.hello = hello;
}
Parent.prototype.sayHello = function(){
console.log(this.hello);
}
function Child(hello,world){
Parent.call(this,hello);//将父类的属性继承过来
this.world = world;//新增一些属性
}
Child.prototype = new Parent();//将父类的方法继承过来
Child.prototype.sayWorld = function(){//新增一些方法
console.log(this.world);
}
var c = new Child("zhangsan","lisi");
c.sayHello();
c.sayWorld();
可以继承通过prototype 添加的方法和属性
为什么呢
因为在子对象的初始化时,加了一个父类型的原型属性回调
Parent.call(this,hello);//将父类的属性继承过来
这样就强行把父类型的原型属性拉了过来
12.call和apply的用法(详细介绍)
js中call和apply都可以实现继承,唯一的一点参数不同,
func.call(func1,var1,var2,var3)
对应的apply写法为:func.apply(func1,[var1,var2,var3])
。
- JS手册中对call的解释:
调用一个对象的一个方法,以另一个对象替换当前对象。
call([thisObj[,arg1[, arg2[, [,.argN]]]]])
参数:thisObj 可选项 默认就是this 被称作当前对象的对象
args 可选项 将被传递方法参数序列
call方法可以用来代替另一个对象调用一个方法,将一个函数的对象上下文,从初始的上下文改变为thisObj指定的新对象。也就是说这个第三方函数对象的上下文this指向的是第一方函数作用链外的那个对象的this。
说简单点,这两个函数的作用其实就是更改调用的函数对象的内部指针,即改变对象的this指向的内容,使调用的第三方函数对象的this指向作用域链外的那个对象的this。
function Person(name,age){ //定义一个类
this.name=name; //名字
this.age=age; //年龄
this.sayhello=function(){alert(this.name)};
}
function Print(){ //显示类的属性
this.funcName="Print";
this.show=function(){
var msg=[];
for(var key in this){
if(typeof(this[key])!="function"){
msg.push([key,":",this[key]].join(""));
}
}
alert(msg.join(" "));
};
}
function Student(name,age,grade,school){ //学生类
Person.apply(this,arguments);//比call优越的地方
Print.apply(this,arguments);
this.grade=grade; //年级
this.school=school; //学校
}
var p1=new Person("卜开化",80);
p1.sayhello();
var s1=new Student("白云飞",40,9,"岳麓书院");
s1.show();
s1.sayhello();
alert(s1.funcName);
//alert
卜开化
name:白云飞 age:40 funcName:Print grade:9 school:岳麓书院
白云飞
Print
另外,Function.apply()在提升程序性能方面有突出作用:
我们先从Math.max()函数说起,Math.max后面可以接任意个参数,最后返回所有参数中的最大值。
alert(Math.max(5,8)); //8
alert(Math.max(5,7,9,3,1,6)); //9
//但是在很多情况下,我们需要找出数组中最大的元素。
var arr=[5,7,9,1];
//alert(Math.max(arr)); // 这样却是不行的。NaN
//要这样写
function getMax(arr){
var arrLen=arr.length;
for(var i=0,ret=arr[0];i<arrLen;i++){
ret=Math.max(ret,arr[i]);
}
return ret;
}
alert(getMax(arr)); //9
//换用apply,可以这样写
function getMax2(arr){
return Math.max.apply(null,arr);
}
alert(getMax2(arr)); //9
//两段代码达到了同样的目的,但是getMax2却优雅,高效,简洁得多。
//再比如数组的push方法。
var arr1=[1,3,4];
var arr2=[3,4,5];
//如果我们要把 arr2展开,然后一个一个追加到arr1中去,最后让arr1=[1,3,4,3,4,5]
//arr1.push(arr2)显然是不行的。 因为这样做会得到[1,3,4,[3,4,5]]
//我们只能用一个循环去一个一个的push(当然也可以用arr1.concat(arr2),但是concat方法并不改变arr1本身)
var arrLen=arr2.length;
for(var i=0;i<arrLen;i++){
arr1.push(arr2[i]);
}
//自从有了Apply,事情就变得如此简单
Array.prototype.push.apply(arr1,arr2); //现在arr1就是想要的结果
//alert 8 9 9 9
13.原型与原型链
js 获取原型的方法?
p.__proto__
p.constructor.prototype
也就是Person.prototypeObject.getPrototypeOf(p)
14.什么是闭包,为什么要用它?
闭包就是有权访问另一个函数作用域内变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以访问到当前函数的局部变量
闭包的两个常见用途
- 在函数外部能够访问到函数内部的变量,可以创建私有变量
- 使已经执行结束的函数上下文中的变量对象继续留在内存中,因为闭包保留了这个变量对象的引用,所以这个变量对象不会被回收。
function a(){
var n = 0;
function add(){
n++;
console.log(n);
}
return add;
}
var a1 = a(); //注意,函数名只是一个标识(指向函数的指针),而()才是执行函数;
a1(); //1
a1(); //2 第二次调用n变量还在内存中
15.了解DOM和BOM吗?
- DOM 文档对象模型 把文档当作对象看待,这个对象主要定义了处理网页内容的方法和接口。
- BOM 浏览器对象模型,把浏览器当作对象看待,主要定义与浏览器进行交互的方法和接口。BOM核心是window,而window对象具有双重角色,既是通过js访问浏览器窗口的接口,又是一个全局对象,也就是在网页中定义的任何对象(变量和函数),都作为全局对象的一个属性或者方法
- window 对象含有 location 对象、navigator 对象、screen 对象等子对象,并且 DOM 的最根本的对象 document 对象也是 BOM 的 window 对象的子对象
16.三种事件模型是什么?
事件是用户和网页的交互
- DOM0级模型:网页中定义监听函数或者用js指定监听
- IE事件模型:
事件处理阶段:执行目标元素绑定的监听事件
事件冒泡阶段:事件从目标元素一直冒泡到document,依次检查经过的结点是否绑定了事件监听函数,有则执行
可以添加多个监听函数,按顺序依次执行 - DOM2级事件模型:
事件捕获阶段:捕获的是事件从document一直向下传播的目标元素,检查经过的结点是否绑定函数事件,如果有则执行。后面两个阶段和IE事件模型相同
17.事件委托是什么?
本质上是利用了浏览器冒泡的机制,因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到目标节点,因为可以把子节点的监听函数定义在父节点上,有父节点的监听函数统一处理各个子元素的事件
也就是父元素对于子元素的事件代理
不必每个元素都绑定监听事件,这样减少了内存的消耗和内存泄漏。
使用事件代理可以实现事件的动态绑定,比如新增节点就不需要为他再添加一个监听事件,发生的事件会交给父元素的监听函数来处理
18.事件传播是什么?
当事件发生再DOM元素上时,该事件并不完全发生再那个元素上
事件传播有三个阶段,
- 捕获阶段 从window开始,然后向下到每一个元素,直到到达目标元素事件或者event.target
- 目标阶段 事件已经到达目标元素
- 冒泡阶段 事件从目标元素冒泡,然后上升到每个元素,知道到达window
19.事件捕获是什么?
当事件发生再DOM元素上时,该事件并不完全发生再那个与那个元素上。在捕获阶段,事件从window开始,一直到触发事件的元素,window–document–html–body–目标元素
例如有下面的HTML结构
有三层继承关系
<div class="grandparent">
<div class="parent">
<div class="child">1</div>
</div>
</div>
我们来写一个模拟事件捕获的js代码
fnunction addEvent(element,event,callback,isCapture = false){
//判断输入是否错误
if(!el || !event ||!callback || typeof callback !== 'function') return;
if(typeof element === 'string'){
element = document.querySelector(element);
};
element.addEventListener(event ,callback ,isCapture);
}
addEvent(document,'DOMContentLoaded',()=>{
const child = document.querySelector('.child');
const parent = document.querySelector('.parent');
const grandparent = document.querySelector('.grandparent');
addEvent(child,'click',function(e){
console.log('child');
}
);
addEvent(parent,'click',function(e){
console.log('parent');
}
);
addEvent(grandparent,'click',function(e){
console.log('grandparent');
}
);
addEvent(document,'click',function(e){
console.log('document');
}
);
addEvent('html', 'click', function (e) {
console.log('html');
})
addEvent(window, 'click', function (e) {
console.log('window');
})
});
addEventlistener方法具有第三个可选参数useCapture,默认值为false,事件将在冒泡阶段中发生,如果为true 则事件将在捕获阶段中发生,如果单击child元素,它将分别在控制台上打印window document html grandparent parent
也就是模拟出了一个事件捕获
20.事件冒泡是什么?
事件冒泡刚好与事件捕获相反,当前元素–body–html–document–window
21.DOM操作 添加移除移动复制创建和查找结点
- 创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
- 添加删除替换插入
appendChild(node)
removeChild(node)
replaceChild(new,old)
insertBefore(new,old)
- 查找
getElementById();
getElementByName();
getElementByTagName();
getElementByClassName();
querySelector();
querySelectorAll();
4.属性操作
getAttribute(key);
getAttribute(key,value);
hasAttribute(key);
document.getElementsByTagName("BUTTON")[0].hasAttribute("onclick");
removeAttribute(key)
22.js数组和字符串原生方法
数组方法
对象方法
23.常用正则表达式
//(1)匹配 16 进制颜色值
var color = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
//(2)匹配日期,如 yyyy-mm-dd 格式
var date = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
//(3)匹配 qq 号
var qq = /^[1-9][0-9]{4,10}$/g;
//(4)手机号码正则
var phone = /^1[34578]\d{9}$/g;
//(5)用户名正则
var username = /^[a-zA-Z\$][a-zA-Z0-9_\$]{4,16}$/;
//(6)Email正则
var email = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
//(7)身份证号(18位)正则
var cP = /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
//(8)URL正则
var urlP= /^((https?|ftp|file):\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
// (9)ipv4地址正则
var ipP = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
// (10)//车牌号正则
var cPattern = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/;
// (11)强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):var pwd = /^(?=.\d)(?=.[a-z])(?=.[A-Z]).{8,10}$/
24.Ajax 是什么? 如何创建一个 Ajax?
一种异步通信的方法,通过直接由 js 脚本向服务器发起 http 通信,然后根据服务器返回的数据,更新网页的相应部分,而不用刷新整个页面的一种方法。
- 原生手写四个步骤
//1:创建Ajax对象
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本
//2:配置 Ajax请求地址
xhr.open('get','index.xml',true);
//3:发送请求
xhr.send(null); // 严谨写法
//4:监听请求,接受响应
xhr.onreadysatechange=function(){
if(xhr.readySate==4&&xhr.status==200 || xhr.status==304 )
console.log(xhr.responsetXML)
}
- jQuery写法
$.ajax({
type:'post',
url:'',
async:ture,//async 异步 sync 同步
data:data,//针对post请求
dataType:'jsonp',
success:function (msg) {
},
error:function (error) {
}
})
- promise 封装实现:
// promise 封装实现:
function getJSON(url) {
// 创建一个 promise 对象
let promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("GET", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function() {
reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置请求头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 请求
xhr.send(null);
});
return promise;
}
25. js 延迟加载的方式有哪些?
js 的加载、解析和执行会阻塞页面的渲染过程,因此我们希望 js 脚本能够尽可能的延迟加载,提高页面的渲染速度。
- 将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。
- 给 js 脚本添加 defer属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来说最后是顺序执行的,但是在一些浏览器中可能不是这样。
- 给 js 脚本添加 async属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程,但是当脚本加载完成后立即执行 js脚本,这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的,一般不会按照代码的顺序依次执行。
- 动态创建 DOM 标签的方式,我们可以对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。
26.谈谈你对模块化开发的理解?
- 一个模块是实现一个特定功能的一组方法。在最开始的时候,js 只实现一些简单的功能,所以并没有模块的概念,但随着程序越来越复杂,代码的模块化开发变得越来越重要。
- 由于函数具有独立作用域的特点,最原始的写法是使用函数来作为模块,几个函数作为一个模块,但是这种方式容易造成全局变量的污染,并且模块间没有联系。
- 后面提出了对象写法,通过将函数作为一个对象的方法来实现,这样解决了直接使用函数作为模块的一些缺点,但是这种办法会暴露所有的所有的模块成员,外部代码可以修改内部属性的值。
- 现在最常用的是立即执行函数的写法,通过利用闭包来实现模块私有作用域的建立,同时不会对全局作用域造成污染。
27.js 的几种模块规范?模块加载方案?
- 第一种是 CommonJS 方案,它通过 require 来引入模块,通过 module.exports 定义模块的输出接口。这种模块加载方案是服务器端的解决方案,它是以同步的方式来引入模块的,因为在服务端文件都存储在本地磁盘,所以读取非常快,所以以同步的方式加载没有问题。但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载的方式更加合适。
- 第二种是 AMD 方案,这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范
- 第三种是 CMD 方案,这种方案和 AMD 方案都是为了解决异步模块加载的问题,sea.js 实现了 CMD 规范。它和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。
- 第四种方案是 ES6 提出的方案,使用 import 和 export 的形式来导入导出模块
28.AMD 和 CMD 规范的区别?
- 第一个方面是在模块定义时对依赖的处理不同。AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块。而 CMD 推崇就近依赖,只有在用到某个模块的时候再去 require。
- 第二个方面是对依赖模块的执行时机处理不同。首先 AMD 和 CMD 对于模块的加载方式都是异步加载,不过它们的区别在于模块的执行时机,AMD 在依赖模块加载完成后就直接执行依赖模块,依赖模块的执行顺序和我们书写的顺序不一定一致。而 CMD在依赖模块加载完成后并不执行,只是下载而已,等到所有的依赖模块都加载好后,进入回调函数逻辑,遇到 require 语句的时候才执行对应的模块,这样模块的执行顺序就和我们书写的顺序保持一致了。
// CMD
define(function(require, exports, module) {
var a = require("./a");
a.doSomething();
// 此处略去 100 行
var b = require("./b"); // 依赖可以就近书写
b.doSomething();
// ...
});
// AMD 默认推荐
define(["./a", "./b"], function(a, b) {
// 依赖必须一开始就写好
a.doSomething();
// 此处略去 100 行
b.doSomething();
// ...
})
29.ES6 模块与 CommonJS 模块、AMD、CMD 的差异。
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。CommonJS 模块输出的是值的,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令 import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。CommonJS 模块就是对象,即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
30. requireJS的核心原理是什么?
require.js 的核心原理是通过动态创建 script 脚本来异步引入模块,然后对每个脚本的 load 事件进行监听,如果每个脚本都加载完成了,再调用回调函数。
标签:function,必背,console,log,对象,JavaScript,var,Interview,prototype 来源: https://blog.csdn.net/weixin_43698328/article/details/110632904