1.冒泡排序:
只需要比较n-1趟,所以循环次数为数组长度-1。第二层循环,因为比较过后得到的最大元素已经放置再末尾所以不参与比较,故arr.length-1-i
function BubbleSort(arr){
for(var i=0;i
2.快速排序:
利用二分法和递归实现快速排序。
function quickSort(arr){
if(arr.length == 0){
return [];
}
//利用Math.floor()方法向下取整找到中间位置
var cIndex = Math.floor(arr.length / 2);
//再用splice()方法将数组中间位置的元素取出来
var c = arr.splice(cIndex,1);
var l = [],r = [];
for(var i = 0;i
3.JS基本规范:
不要在同一行声明多个变量,正确写法如下:
var a = 1,
b = 2,
c = 3;
建议使用对象字面量的写法替代new Array这种写法:var arr = [1,2,3,4];
尽量不要使用全局变量;
For循环和IF语句必须使用大括号;
Switch语句必须带有Default分支;
for-in 循环中的变量应该使用Var关键字限定作用域,避免作用域污染
4.JS的基本数据类型
undefined、Null、String、Boolean、Number
5.Js中数组的一些操作
<1>map():遍历数组,返回回调返回值组成的新数组
var arr = [1,2,3,4];
var arr = arr.map(function(item){
return item * item;
})
console.log(arr); //[1,4,9,16]
var users = [
{name:"老王",age: 70},
{name:"大王",age: 50},
{name:"小王",age: 30}
];
var ages = users.map(function(user){
return user.age;
})
console.log(ages); //[70,50,30]
<2>forEach():遍历数组,但无法break,可以用try catch语句中的throw new Error()来停止
var users = [
{name:"老王",age: 70},
{name:"大王",age: 50},
{name:"小王",age: 30}
];
var names = [];
users.forEach(function (item){
names.push(item.name);
});
console.log(names); //["老王", "大王", "小王"]
<3>filter():过滤
var arr = [1,2,3,4,5];
var newArr = arr.filter(function(item){
return item>3;
});
console.log(newArr); //[4,5]
<4>some():有一项返回true,则整体都为true
var arr = [1,2,3,4,5];
var result = arr.some(function(item){
return item>4;
});
console.log(result); //true
<5>every():有一项返回false,则整体返回false
var arr = [1,2,3,4,5];
var result = arr.every(function(item){
return item>6;
});
console.log(result); //false
<6>join():通过指定连接符生成字符串
var arr = [1,2,3,4,5];
var result = arr.join("-=-");
console.log(result); //1-=-2-=-3-=-4-=-5
<7>push()/pop():末尾推入和弹出,改变原数组, 返回推入/弹出项,会修改原数组
var arr = [1,2,3,4];
var result1 = arr.push(5);
console.log(result1) //5
console.log(arr); //[1,2,3,4,5]
var result2 = arr.pop();
console.log(result2); //5
console.log(arr); //[1,2,3,4]
<8>shift()/unshift():
shift()移除数组第一项,返回该值;
unshift()添加一个元素进数组第一项,返回新数组长度
var arr = [1,2,3,4];
var result1 = arr.shift();
console.log(result1); //1
console.log(arr); //[2,3,4]
var result2 = arr.unshift(1);
console.log(result2); //4
console.log(arr); //[1,2,3,4]
<9>sort(fn)/reverse():排序数组和反转数组,会修改原数组
var arr = [1,6,3,5,4,2];
arr.sort(function(a,b){
return a-b; //a-b由小到大排序,b-a由大到小排序
});
console.log(arr); //[1,2,3,4,5,6]
arr.reverse();
console.log(otherResult); //[6,5,4,3,2,1]
<10>concat():连接数组,不影响原数组浅拷贝。如果参数不是数组,直接当成数组元素添加到数组中,并返回一个新的数组实例。
var arr1 = [1,2,3];
var arr2 = ["Sami","Nick","Michael"];
var result = arr1.concat(arr2);
console.log(arr1); //[1,2,3]
console.log(arr2); //["Sami","Nick","Michael"]
console.log(result); //[1,2,3,"Sami","Nick","Michael"]
<11>slice(start,end):返回截断后的新数组,不改变原数组。第一个参数起始位置,第二个结束位置。截取的数组不包含结束位置上的元素。
var arr = [1,2,3,4,5];
var result = arr.slice(0,2);
console.log(result); //[1,2]
console.log(arr); //[1,2,3,4,5]
<12>splice(start,number,value...):返回删除元素组成的数组,value 为插入项,改变原数组。
var arr = [1,2,3,4,5];
var result = arr.splice(1,2);
console.log(arr); //[1,4,5];
console.log(result); //[2,3];
<13>indexOf(value,fromIndex)、lastIndexOf(value,fromIndex):
查找数组项,返回对应的下标。indexOf从前往后查找,lastIndexOf从后往前查找。
var arr = [1,2,7,4,5,6,7];
var index = arr.indexOf(7);
console.log(index); //2
var index = arr.indexOf(7,3);
console.log(index); //6
var index = arr.lastIndexOf(7);
console.log(index); //6
var index = arr.lastIndexOf(7,3);
console.log(index); //2
6.事件侦听器通用对象:
var EventUtil = {
addEvent:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent("on" + type,handler);
}else{
element["on" + type] = handler;
}
},
removeEvent:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent("on" + type,handler);
}else{
element["on" + type] = null;
}
},
getEvent:function(){
return event ? event: window.event;
},
getTarget:function(event){
return event.target || event.srcElement;
},
preventDefault:function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
},
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
}
//调用方法
var btn = document.getElementById("myBtn");
var handler = function(){
event = EventUtil.getEvent();
EventUtil.preventDefault();
alert("Clicked");
};
EventUtil.addEvent(btn,click,handler);
7.JS的内置对象:
数据封装对象:Object、Array、Boolean、Number、String;
其他对象:Math、Date、Function、Error、RegExp、Arguments
8.闭包:指有权访问另一个函数作用域中变量的函数。
function parentFunc(){
var a = 1;
function childFunc(){
console.log(a);
}
return childFunc();
}
闭包的特征:
<1>函数内再嵌套函数;
<2>内部函数可以调用外部函数的参数和变量;
<3>参数和变量不会被垃圾回收机制回收。
闭包的好处:能够实现封装和缓存。使用闭包主要是为了封装对象的私有属性和私有方法,闭包可以避免全局变量的污染。
闭包的缺点:闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄漏。
闭包经典问题:
function parentFunc(){
var arr = [];
for(var i = 0;i<5;i++){
arr[i] = function (){
return i;
}
}
return arr;
}
console.log(parentFunc()[0]()); //5
console.log(parentFunc()[1]()); //5
这里就展现出了几个关键信息,首先分析一下代码:循环中创建了一个匿名函数并将其赋值给arr数组中对应索引的元素,匿名函数作用是返回i值。此时,arr数组中存放的是匿名函数,而匿名函数还没有执行。当调用parentFunc()函数时返回arr数组,再单独执行数组中的元素保存的匿名函数,此时循环已经执行完,所以i值为5。接下来再去调用其它数组元素中的匿名函数也样会获得数值5。
要解决这个闭包所产生的问题,有两种办法:
<1>立即执行匿名函数
function parentFunc(){
var arr = [];
for(var i = 0;i<5;i++){
arr[i] = (function (){
return i;
})();
}
return arr;
}
console.log(parentFunc()); //[0,1,2,3,4]
<2>使用let关键字声明变量:使用let声明变量会形成块级作用域
function parentFunc(){
var arr = [];
for(let i = 0;i<5;i++){
arr[i] = function (){
return i;
};
}
return arr;
}
console.log(parentFunc()[0]()); //0
9.JS作用域:分为全局作用域和函数作用域
全局作用域,代码在程序中的任何地方都能访问,window对象的内置属性都拥有全局作用域;
函数作用域,在固定的代码片段才能访问。
10.作用域链:作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。
11.原型和原型链:
每个构造函数都会在其内部初始化一个属性-prototype(原型)。当我们访问一个对象的属性时,如果这个对象的内部不存在这个属性,就会去其构造函数的prototype中,也就是对象的__proto__属性中查找这个属性,这个prototype又会有自己的prototype,于是就会像链条一样一直找下去形成原型链。
(因为所有的对象都是由Object对象继承而来,因此最终查找到Object的prototype结束)
12.组件化:利用组件化思想将多个页面都需要用的功能组件封装起来,提高代码复用性,降低耦合性,增强可维护性和可读性。
13.模块化:主要用途是封装对象
模块化的优点:避免全局变量变量污染,命名冲突;提高代码复用率;提高了可维护性。
最常用的模块化封装对象的方法是:构造函数模式+原型模式。
构造函数内写属性,原型中放方法和重写构造函数指针。
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person,
sayName: function(){
alert(this.name);
}
}
var person1 = new Person("老王", 70);
var person2 = new Person("小王", 20);
alert(person1.name === person2.name); //false,构造函数内属性不公用
alert(person1.sayName === person2.sayName); //true,原型中的方法共用
组合使用构造函数模式和原型模式封装对象的好处在于,每个新建的实例都拥有自己的属性,然后共同享有原型中的方法,不用每次创建新实例都重新创建同样的方法。
14.继承:实现继承的常用方法是原型链+借用构造函数。
原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承。
function SuperType(name){
this.name = name;
}
SuperType.prototype.sayName = function(){
alert(this.name);
}
function SubType(age,name){
//继承属性
SuperType.call(this,name);
this.age = age;
}
//继承方法
SubType.prototype = new SuperType()
/*
*最优写法,因为仅仅只是继承方法而不继承属性,不用new多一次父类
*SubType.prototype = Object.create(SuperType.prototype);
*/
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
}
var p1 = new SubType("老王", 70);
var p2 = new SubType("小王", 20);
p1.sayName(); //老王
p2.sayName(); //小王
p1.sayAge(); //70
p2.sayAge(); //20
因为SuperType.prototype.constructor重写了SubType.prototype.constructor,导致子类的实例中的constructor属性指向了父类,所以在继承了父类方法后,要重写一下子类原型对象中的构造器。
15.Ajax:ajax的核心是XMLHttpRequest(XHR)
<1>如何创建一个ajax
//get方法
var xhr = new XMLHttpRequest();
xhr.open("get","example.php",true); //发送的请求类型、请求的URL、是否异步发送请求
xhr.send(null);
xhr.onreadystatechange= function(){
if(xhr.readyState === 4){
if(xhr.status === 200){
success(xhr.responseText);
}else{
console.log(xhr.status);
}
}
}
//post方法
var data = new FormData(document.forms[0]);
xhr.open("post","example.php",true);
xhr.send(data);
<2>同步和异步的区别:
同步:用户请求,等待,响应,刷新页面展示内容再操作;
异步:用户请求的同时可继续对页面操作,响应完成不刷新页面展示新内容。
<3>
Ajax优点:
异步请求响应快,用户体验好;页面无刷新、数据局部更新;按需取数据,减少了冗余请求和服务器的负担;
Ajax缺点:
异步回调问题、this指向问题、路由跳转back问题;对搜索引擎的支持比较弱,对于一些手机还不是很好的支持
<4>post一般用于修改服务器上的资源,对发送的数据没有限制;而get一般用于请求获取数据。
16.事件代理:
事件代理又称之为事件委托,是绑定事件的常用技巧。即把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。
事件代理的原理是DOM元素的事件冒泡。
使用事件代理的好处是可以提高性能,节省内存占用,减少事件注册。可以实现当新增子对象时无需再对其绑定。
var list = document.getElementById("myLinks");
//EventUtil事件监听器通用对象可以去前面看
EventUtil.addEvent(list,"click",function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
switch(target.id){
case "doSomething":
document.title = "I changed";
break;
case "goSomeWhere":
location.href = "http://www.baidu.com";
break;
case "sayHi":
alert("HI");
break;
}
});
17.this对象:
·this总是指向函数的直接调用者(而非间接调用者);
·如果有new关键字,this指向new出来的新对象
·在事件中,this指向触发这个事件的对象,但IE中的attachEvent中的this指向window对象。
18.事件模型:
·冒泡型事件:当你使用事件冒泡时,子元素先触发,父元素后触发;
·捕获型事件:当你使用事件捕获时,父元素先触发,子元素后触发;
·DOM事件流:同时支持两种事件模型,冒泡型事件和捕获型事件;
·阻止冒泡:阻止事件冒泡。在w3c中使用stopPropagation()方法,在IE中使用cancelBubble = true;
·阻止捕获:阻止事件的默认行为。在w3c中使用preventDefault()方法,在IE中使用returnValue = false。
19.XML和JSON的区别:
·JSON相对XML,数据体积更小,传递速度更快些;
·JSON与JS的交互更方便,更容易解析处理,更好的数据交互;
·JSON对数据的描述性比XML较差;
·JSON的传输速度远远快于XML。
20.JS定义对象的方法:
·对象字面量: var obj = {name:'obj'};
·new内置构造函数: var obj = new Object({name:'obj'})
·new自定义构造函数:
function Person(name){
this.name = name;
}
var obj = new Person('obj')
·Object.create: var obj = Object.create(object.prototype);
var o1 = {name: 'o1'};
var o2 = Object.create(o1);
console.log(o2.__proto__ === o1); //true
console.log(o2); //Object{}
//也就是说Object.create()创建的实例本身是没有什么方法的,全部都在原型对象上,所以直接打印实例是没有内容的。
21.Promise:
·promise用来进行延迟和异步计算
·promise的四种状态:pending(初始状态)、fulfilled(成功的操作)、rejected(失败的操作)、settled(promise已被fulfilled或者rejected)
·Promise的构造函数:
var promise = new Promise(function(resolve,reject){
if(...){
resolve(result);
}else{
reject(errMessage);
}
});
·Promise的then方法:
promise.then(onFulfilled,onRejected);
这两个参数分别对应resolve和reject传过来的结果。
·引用廖雪峰老师的两个例子给大家理解一下promise(稍作修改):
new Promise(function(resolve,reject){
console.log("promise start...");
var timeout = Math.random()*2; //0-2
console.log("set timeout to:" + timeout + "seconds");
setTimeout(function(){
if(timeout < 1){
console.log("call resolve()...");
resolve("200 ok");
}else{
console.log("call reject()...");
reject("timeout in" + timeout + "seconds");
}
},timeout * 1000);
}).then(function(resolve){
console.log("Done:" + resolve);
}).catch(function(reject){
console.log("Failed:" + reject)
});
可见Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了。
Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。
要串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:
job1.then(job2).then(job3).catch(handleError);
function multi(input){
return new Promise(function(resolve,reject){
console.log('计算 ' + input + ' x ' + input + '...');
setTimeout(resolve,500, input * input);
});
}
function add(input){
return new Promise(function(resolve,reject){
console.log('计算 ' + input + ' + ' + input + '...');
setTimeout(resolve,500,input + input);
});
}
var p = new Promise(function(resolve,reject){
console.log("start ....");
resolve(33);
});
p.then(multi)
.then(add)
.then(multi)
.then(add)
.then(function(result){
console.log("结果:" + result);
});
22.eval()的作用:
把对应的字符串解析成可执行的JS代码并运行;
应该避免使用eval(),不安全且非常耗性能。
23.Null和Undefined的区别:
undefined表示声明的变量未赋值,而null表示声明变量的值为空值;
两者相比较时要使用===,因为==无法区分。
24.["1", "2", "3"].map(parseInt) 答案是多少?
[1,NaN.NaN]。因为parseInt的参数时(val,radix),radix表示基数(多少进制),而map的参数是(function(currentValue,index,arr),thisIndex)。所以map传了三个参数给parseInt,radix对应index不合法导致解析失败。
25.JSON与字符串的转换:
·字符串转换为JSON
var obj = eval('(' + str + ')');
var obj = str.parseJSON();
var obj = JSON.parse(str); //最常用
·JSON转换为字符串
var str = obj.toJSONString();
var str = JSON.stringify(obj);
26.attribute和property的区别:
attribute是DOM元素再文档中作为html标签拥有的属性;
property是DOM元素再JS中作为对象所拥有的属性;
27.如何判断一个对象是否为数组:
function isArray(obj){
if(typeof obj === 'object'){
return Object.prototype.toString.call(obj) === '[Object Array]';
}
return false;
}
28.event loop(事件循环):
·JS是一门单线程的非阻塞的脚本语言,单线程意味着JS在执行代码的任何时候,都只有一个主线程来处理所有任务。
同步和异步任务分别进入不同的执行场所,同步任务进入主线程,异步任务进入Event table并注册函数;
当指定的事件完成时,Event table会将这个函数移入事件队列Event queue中;
主线程的任务执行完毕为空,会去Event queue读取对应的函数,进入主线程中执行;
上诉的过程不断重复,也就是我们说的事件循环Event loop。
·举个例子说明他们的执行顺序:
console.log("1");
setTimeout(function(){
console.log("2");
},0);
console.log("3"); //输出结果为:1,3,2
因为setTimeout是异步任务,其他两个console同步任务按顺序执行,所以setTimeout最后输出。
·微任务和宏任务,结合例子说明:
console.log('1');
setTimeout(() => {
console.log('2')
}, 1000);
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3');
}, 0);
console.log('4');
resolve();
console.log('5');
}).then(() => {
console.log('6');
});
console.log('7'); //执行结果为1,4,5,7,6,3,2
先来说明下什么是微任务和宏任务,他们都是异步的任务,且都属于队列,区别在于微任务先于宏任务执行。(有一点歧义,之后再说)
宏任务包含有:setTimeout、setInterval、setImmediate、I/O、UI rendering、DOM事件;
微任务包含有:process.nextTick()、promise.then、MutationObserver;
补充一点 new promise会同步执行。
在执行到new Promise
的时候会立马新建一个promise对象并立即执行
。所以会输出 1,4,5,7
,而then则会在Event Table
中注册成回调函数并放在微任务队列中,而两个setTimeout(输出3)和setTimeout(输出2,1s后完成)
会被先后注册成回调函数并放在宏任务队列中。
·复杂测试题理解程度(分清宏任务和微任务,画出队列执行顺序理解):
console.log(1)
process.nextTick(() => {
console.log(8)
setTimeout(() => {
console.log(9)
})
})
setTimeout(() => {
console.log(2)
new Promise(() => {
console.log(11)
})
})
let promise = new Promise((resolve,reject) => {
setTimeout(() => {
console.log(10)
})
resolve()
console.log(4)
})
fn()
console.log(3)
promise.then(() => {
console.log(12)
})
function fn(){
console.log(6)
} //输出结果:1,4,6,3,8,12,2,11,10,9
按顺序执行,同步任务先执行,再到微任务和宏任务,其内部包含的亦是如此。(不同评论留言)
这些内容来自:https://juejin.im/post/5d2603...
29.附加多一题promise+event loop的题目:贼有意思
new Promise((resolve,reject)=>{
console.log("promise1")
resolve()
}).then(()=>{
console.log("then11")
new Promise((resolve,reject)=>{
console.log("promise2")
resolve()
}).then(()=>{
console.log("then21")
}).then(()=>{
console.log("then23")
})
}).then(()=>{
console.log("then12")
})
new Promise((resolve,reject)=>{
console.log("promise3")
resolve()
}).then(()=>{
console.log("then31")
})
//输出结果:[promise1,promise3,then11,promise2,then31,then21,then12,then23]
不明白的来评论问我!链式调用插队问题。
30.上瘾了,加多一题async await + event loop + promise
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log( 'async2');
}
console.log("script start");
setTimeout(function () {
console.log("settimeout");
},0);
async1();
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});
console.log('script end');
async/await仅仅影响的是函数内的执行,而不会影响到函数体外的执行顺序。也就是说async1()并不会阻塞后续程序的执行,await async2()
相当于一个Promise,console.log("async1 end");
相当于前方Promise的then之后执行的函数。
最终输出结果:[script start
,async1 start
,async2
,promise1
,script end
,async1 end
,promise2
,settimeout
]
31.Math.random()函数:生成[0,1)的随机数
·结合Math.floor()方法获得整数,向下取整
·Math.ceil()方法向上取整
·Math.round()方法四舍五入
<1>生成[0,10)之间的整数:
Math.floor(Math.random()*10);
<2>生成[0,10]之间的整数(也就是[0,11)):
Math.floor(Math.random()*(10+1));
<3>生成[1,10]之间的整数:
Math.floor(Math.random()*(10+1)+1);
32.类型判断:
三种方式,分别为:typeof instanceof、Object.prototype.toString.call();
<1>typeof:判断属于哪一种基本类型。
typeof 'str' //string
typeof true //boolean
typeof 10 //number
typeof null //object 所以无法判定是否为null
typeof undefined //undefined
typeof Symbol() //symbol
typeof [] //object
typeof {} //object
typeof (()=>{}) //function
<2>instanceof:可以对对象类型进行判定,其原理就是测试构造函数的protoype是否出现被检测对象的原型链上。
[] instanceof Array //true
{} instanceof Object //true
(()=>{}) instanceof Function //true
但是,instanceof并不是最好的,也有bug,如下:
let arr = [];
let obj = {};
arr instanceof Array //true
arr instanceof Object //true
obj instanceof Object //true
obj instanceof Array //false
因为instanceof的是通过原型链实现的:
arr.__proto__ === Array.prototype;
Array.prototype.__proto__ === Object.prototype;
arr.__proto__ ===Object.prototype;
所以arr即使继承自Array,也继承自Object,因此instanceof检测arr是否属于Array和Object都是true。
<3>Object.prototype.toString.call():
Obejct.prototype.toString.call({}) //[object Object]
Object.prototype.toString.call([]) //[object Array]
Object.prototype.toString.call(()=>{}) //[object Function]
Object.prototype.toString.call('str') //[object String]
Object.prototype.toString.call(1) //[object Number]
Object.prototype.toString.call(Symbol())//[object Symbol]
Object.prototype.toString.call(null) //[object Null]
Object.prototype.toString.call(undefined) //[object Undefined]
Object.prototype.toString.call(new Date()) //[object Date]
//以此类推,new Set()、new Map()等等都一样
·Array.isArray()也可以判定是否为数组,但是IE6-8不兼容所以可以写一个兼容方法
33.空值判断:
<1>先来个反面例子
if([]){
console.log("true");
}else{
console.log("false");
}
//返回true
if({}){
console.log("true");
}else{
console.log("false");
}
//返回true
因为在这里if只是判断这个对象存不存在,[]、{}对象明显是存在的,所以返回true,而并不能判断数组和对象是否为空。
<2>空数组判断:
Array.length>0 即可
<3>空对象判断:
·Object.getOwnPropertyNames(),返回值是对象中属性名组成的数组
var obj = {};
Object.getOwnPropertyNames(obj).length === 0; //true
·JSON对象转换为字符串,然后与字符串"{}"比较。因为JSON对象最外层为{}。
var obj = {};
var a = JSON.stringify(obj);
console.log(a === "{}"); //true
·for..in..循环判断:
var obj = {};
function isNullObj(obj){
for(let key in obj){
return false;
}
return true;
};
console.log(isNullObj(obj)); //true
·Object.Keys().ES6的新方法,返回值是属性名组成的数组。
var obj = {};
console.log(Object.Keys(obj).length === 0); //true
34.字符串常用函数:
<1>substr()方法把字符串分割成数组,不改变原字符串。
substr(start,number)的参数分别是其实位置和截取的数量。用法也一样,当start为负值的时候,开始位置为length+start,依然向后截取
var str = "你好啊小伙子";
var result1 = str.substr(1,2);
var result2 = str.substr(-2,2);
console.log(result1 + "\n" + result2); //"好啊" "伙子"
<2>split(separator,howmany),将字符串分割成数组,不改变原字符串:
var str = "你好啊,小伙子";
var res1 = str.split(",");
var res2 = str.split("");
var res3 = str.split("",3);
console.log(res1); //["你好啊", "小伙子"]
console.log(res2); //["你", "好", "啊", ",", "小", "伙", "子"]
console.log(res3); //["你", "好", "啊"]
35.对象常用方法:
<1>Object.prototype.hasOwnProperty(),仅再目标属性为对象自身属性时返回true,若该属性是从原型链继承而来或根本不存在时返回false。
var obj = {age:18};
//记得传参加引号
obj.hasOwnProperty('age'); //true
obj.hasOwnProperty('toString'); //false,继承Object而来
<2>Object.create(obj,desc)(ES6),该方法用于创建一个新对象,并为其设置原型。
var parent = {
hi: 'Hello'
};
var o = Object.create(parent, {
prop: {
value: 1
}
});
console.log(o.prop); //1
这个函数迟点再补充详细的探讨。
36.访问元素的样式的方法:dom.style、window.getComputedStyle(dom,null)、dom.currentStyle;
<1>dom.style属性
var myDiv = document.createElement("div");
myDiv.style.backgroundColor = "red";
console.log(myDiv.style.backgroundColor); //red
dom.style仅可以进行DOM元素内联样式
的读写操作。对于嵌入样式和外部样式无法进行读取操作。
<2>window.getComputedStyle(dom,null)方法:
第一个参数是DOM元素,第二个参数不是必须的,当不查询伪类元素的时候可以忽略或者传入 null。
var myDiv = document.getElementById("myDiv");
var computedStyle = window.getComputedStyle(myDiv,null);
console.log(computedStyle.width);
console.log(computedStyle.color);
window.getComputedStyle()方法可以对行内样式、嵌入样式、外部样式进行读
操作,不可以进行写操作。
我们可以通过使用getComputedStyle读取样式,通过element.style修改样式。
<3>dom.currentStyle:仅支持IE,且同样仅支持读操作,不可进行写入
var myDiv = document.getElementById("myDiv");
var currStyle = myDiv.currentStyle;
console.log(currStyle.width);
console.log(currStyle.color);
37.计算元素大小:
<1>dom.offsetWidth/offsetHeight/offsetLeft/offsetTop
·dom.offsetWidth:DOM元素整体的实际宽度,包含边框、滚动条等边线。(content+padding+border);
·dom.offsetLeft:DOM元素左边框至包含元素的左内边框的距离。
<2>dom.clientWidth/clientHeight
·dom.clientWidth:DOM元素内容的可视区的宽度,不包含边框、滚动条等边线。(content+padding)溢出可视区部分也不计入
宽度。
<4>深入理解区分:
·元素内无内容或者内容不超过可视区域,滚动不出现或不可用的情况:
scorllWidth = clientWidth, offsetWidth为元素实际宽度
·元素内容超过可视区域,滚动条出现的情况:
scrollWidth > clientWidth;
scrollWidth为实际内容的宽度、clientWidth为内容可视区域的宽度、offsetWidth为元素的实际宽度;
<5>dom.getBoundingClientRect():用来确定元素大小
返回值是一个DOMRect对象,用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性。
var myDiv = document.getElementById("myDiv");
var rect = myDiv.getBoundingClientRect();
console.log(rect.top); //div上边到视窗上边的距离
console.log(rect.right); //div右边到视窗左边的距离
console.log(rect.bottom); //div下边到视窗上边的距离
console.log(rect.left); //div左边到视窗左边的距离
38.DOM操作:增、删、查、替换、创建、复制、移动、插入等
<1>创建节点:document.createElement(nodeName)
var myDiv = document.createElement("div"); //创建标签节点
var text = document.createTextNode("文本节点!"); //创建文本节点
<2>添加节点:dom.appendChild(nodeObj)
var newNode = document.createElement("p");
myDiv.appendChild(newNode);
<3>删除节点:dom.removeChild(childNodeObj)
myDiv.removeChild(newNode);
<4>替换节点:dom.replaceChild(newNode,oldNode)
var myDiv = document.getElementById("myDiv");
var oldEle = document.createElement("p");
var newEle=document.createElement("div");
myDiv.replaceChild(newEle,oldEle);
<5>插入节点:dom.insertBefore(newItem,existingItem
)
myDiv.insertBefore(newEle,oldEle);
<6>复制节点:dom.cloneNode(boolean)
var cEle = myDiv.cloneNode(true);//深度复制,复制节点下面所有的子节点
cEle = myDiv.cloneNode(false);//只复制当前节点,不复制子节点
<7>查找节点:document.getElementById/getElementsByClassName/getElementsByTagName()
document.getElementById("id");// 通过id查找,返回唯一的节点
document.getElementsByClassName("class");// 通过class查找,返回值为nodeList类型
document.getElementsByTagName("div");// 通过标签名查找,返回值为nodeList类型
39.DOM事件级别:
·DOM0级:element.onclick = function(){};
·DOM2级:element.addEventListener('click',function(){},false);
false代表冒泡截断触发,true代表捕获截断触发;
·DOM3级:element.addEventListener('keyup',function(){},false);
DOM3级就是增加了一些鼠标、键盘事件。
40.描述一下捕获流程:
window最先触发事件->document->html(document.documentElement)->body->....各层级元素->目标元素(例如button等)
41.事件对象Event最常见的应用:
·event.preventDefault():阻止默认行为(捕获)
·event.stopPropagation():阻止事件冒泡
·event.stopImmediatePropagation():阻止同个元素的默认后续同类型事件的触发。比如一个DOM元素定义了两个click,在第一个click事件中调用了此方法,那么后面的click事件就不会触发。
·event.currentTarget:实际注册事件的元素。一般和事件委托结合使用,因为使用事件委托时currentTarget不是目标元素(比如你点击的按钮)。
·event.target:目标元素。例如你点击触发事件的按钮。
42.自定义事件:
使用的方法:new Event()创建事件对象、dom.dispatchEvent()触发自定义事件。
var eventObj = new Event("custome");
dom.addEventListener("custome",function(){
console.log("custome");
},false);
dom.dispatchEvent(eventObj);
还可以使用new CustomEvent()创建事件对象,这个方法可以传参数:
var custom = new CustomEvent('test_event',{
detail:{
e_name : " this is a test "
}
});
// 某个dom元素监听自定义事件
let div = document.createElement("DIV");
div.addEventListener("test_event",function(e){console.log(e.detail.e_name)},false)
// 触发自定义事件(dispatchEvent 除非事件的参数是必填项,且必须为事件对象)
div.dispatchEvent(custom);// this is a test
43.DOM事件流:
DOM事件流规定包括三个阶段:事件捕获阶段、出于目标阶段和事件冒泡阶段。先触发事件捕获,然后是实际的目标接收到事件,最后一个阶段是冒泡阶段。
44.prototype属性和__proto__属性
·prototype属性是构造函数才拥有的,指向构造函数的原型对象。例如Array.prototype就指向它的原型对象
·__proto__属性是实例拥有的,指向它的构造函数的原型对象。例如new Array().__proto__ === Array.prototype;
这里还有个小问题,虽然__proto__是实例特有的,但是构造函数也是一个实例,所以他也拥有__proto__属性。例如Array.__proto__ === Function.prototype,因为此时Array是一个构造函数是通过Function继承而来的。
45.