JavaScript面试(-------------------------------------------)
this是什么
—大多语言中,’this’代表由类实例化的当前对象。在JavaScript中,’this’通常表示’拥有’方法的对象,但具体取决于一个函数被调用的方式。
方法调用模式
当一个函数被保存为对象的一个属性并调用该方法时,this被绑定至该对象。即使用”obj.method”形式
var val = "outer";
var methodCall = {
val: "inner",
printVal: function(){
console.log(this.val);
}
}
methodCall.printVal(); //"inner"
构造器调用模式
类似面向对象语言中对象的概念,使用new后,this被绑定至该实例。
var val = "outer";
function methodCall(){
this.val = "inner";
}
methodCall.prototype = {
printVal: function(){
console.log(this.val);
}
}
var a = new methodCall();
a.printVal(); //"inner"
函数调用模式
函数未作为对象的属性时,其当作一个正常函数来调用,此时this被绑定至全局对象。
var val = "outer";
function methodCall(){
this.val = "inner";
}
methodCall.prototype = {
printVal: function(){
var that = this;
var innerPrintVal = function(){
console.log(that.val);
console.log(this.val)
}
innerPrintVal();
}
}
var a = new methodCall();
a.printVal();
打印结果为
inner
outer
- a.printVal调用printVal的方式为构造器调用模式,这时的this被绑定到a的作用域。将其赋给that,此时that就代表了a的作用域。
- a.printVal调用innerPrintVal的方式为函数调用模式,这时的this被绑定到全局作用域。
注意,在a.printVal中,this是被绑定在a的作用域上的。但是在调用innerPrintVal时,this在该函数中被再一次绑定到全局作用域了。因此在innerPrintVal中调用console.log(this.val)打印的结果为”outer”
apply/call模式
手动指定this的值
var val = "outer"; function printVal(){
console.log(this.val);
}
function methodCall(){
this.val = "inner";
}
methodCall.prototype = {
printVal: function(){
console.log(this.val);
}
}
var a = new methodCall();
a.printVal(); //"inner"
a.printVal.call(a); //"inner"
printVal.call(a); //"inner" printVal(); //"outer"
printVal.apply(window); //"outer"
a.printVal.apply(window); //"outer"
输出结果为:
inner
inner
inner
outer
outer
outer
可以看出,无论是采用构造器调用模式的a.printVal或是采用函数调用模式的printVal,this均会被apply/call绑定为手动指定的值
1、查找作用域
当前函数在哪个作用域下定义的,那么他的上级作用域就是谁 , 和函数在哪执行没有任何关系
//作用域实例
var num = 12;
function fn(){
var num = 120;
return function(){
console.log(num);
}
}
var f = fn();
f();//->120
(function(){
var num = 1200;
f();//->120
}())
2、++i 和 i++的区别
//实例说明
var i = 5;
5 + i++ //->10
//i++ 是先运算i本身再加1
//========
var j = 5;
5 + (++j) //->11
//++j 是本身先加1再运算
//面试题
var n = 2;
var num = 5 + (++n) + (n++) + (n++) + (++n); // - > 21
3、预解释
在js执行之前,浏览器首页会把带var和function的关键字进行提前申明或定义;var(只是提前申明) function(提前申明+定义)
注:如果在全局作用域下定义一个 num = 10(没有var)和带var 是有区别的
//实例说明
console.log(num);//->undefined
console.log(num1);//->num1 is not defined
var num = 10;//->先预解释->window.num = 10
num1 = 10;//->window.num1 = 10
//不带var的num1没有提前申明所以报is not definend
预解释面试题
//实例1
function a(b){
alert(b);
function b(){
alert(b);
}
b();
}
a(1); //1->alert(b函数),2->alert(函数)
//实例2
alert(a); //undefined
var a = 0;
alert(a); //0
function fn(){
alert(a); //0;因为没var, 所以这里的a会被看作是全局的,往上查找,找到a=0,所以是0,如果全局也没有就会报错
a = 2;
alert(a); //2
}
fn()
alert(a); //2,fn把这全局的a修改了
//实例3
<script>
alert(a);//报错,因为遇到<script>标签对时,会先对这一块进行预解析,运行到下面的才会再进行预解析,下面没预解析,所以找不到a,于是报错了
</script>
<script>
alert(a); //undefined
var a = 0;
alert(a); //0
</script>
4、区分this
函数执行,首先看函数名是否有".",有的话,"."前面是谁this就是谁;没有的话this就是window
自执行函数的this永远是window
给元素的某一个事件绑定方法,当事件触发的时候,执行对应的方法,方法中的this是当前的元素
构造函数中的this是这个类的实例
this还可以通过call、apply、bind来改变
function fn(){
console.log(this);
}
var obj = {
name:"李四",
writeJs:fn
}
obj.writeJs();//this->obj
var fn = obj.writeJs;
fn();//this->window
function sum() {
fn();//this->window
}
sum();
(function() {
fn();//this->window
)()
document.getElementById('div').onclick = fn;//this->#div
document.getElementById('div').onclick = function() {
//this->#div
fn();//this->window
}
this综合实例(360面试题)
var num = 20;
var obj = {
num: 30,
fn: (function (num) {
this.num *= 3;
num += 15;
var num = 45;
return function () {
this.num *= 4;
num += 20;
console.log(num);
}
}) (num)
}
var fn = obj.fn;
fn();//65
obj.fn();//85
5、单例模式
描述同一个事物(同一个对象)的属性和方法放在一个内存空间下,起到分组的作用,这样不同事物之间的属性名相同,相互也不会发生冲突,我们把这种分组编写代码的模式叫做“单例模式”;
var obj = {
name: '张三',
age: '18',
writeJs: function(){
console.log('my is '+this.name+' can write js');
}
}
obj.writeJs();
注:obj又叫做“命名空间”,单例模式项目开发经常使用,我们可以使用单例模式进行模块化开发;
6、工厂模式
单例模式虽然能解决分组作用,但是不能实现批量生产,属于手工作业模式;
工厂模式->“函数的封装”,“低耦合高内聚”:减少页面中的冗余代码,提高代码的重复利用
function createJs(name,age){
var obj = {};
obj.name = name;
obj.age = age;
obj.writeJs = function(){
console.log('my is '+ this.name +' can write js');
}
return obj;
}
var zhangsan = createJs('张三','18');
zhangsan.writeJs();
所有的编程语言都是面向对象开发的。就有类的继承、封装、多态
继承:子类继承父类的属性和方法
封装:函授的封装
多态:当前方法的多种形态
后台语言中的多态包含重载和重写。js中的多态不存在重载,方法名一样,后面的会把前面的覆盖掉,最后只保留一个方法。(js中有一个类似重载但不是重载:可以根据传递的参数不一样,实现不同的功能)重写:子类重写父类的方法
7、构造函数模式
构造函数是通过new关键词创建一个实例;var ex = new CreateJs();其中ex就是CreateJs的实例,生成CreateJs这个类;
Js中所有的类都是函数数据类型,它通过new执行变成一个类,但是他本身也是个普通的函数
Js中所有的实例都是对象数据类型
在构造函数模式中,类中出现的this.xxx=xxx中this是当前类的一个实例
不同实例之间方法不一样(下例)
在构造函数模式中,浏览器会默认把我们的实例返回(返回对象数据类型的值);如果我们手动写return返回;
如果ruturn是一个基本数据类型的值,当前实例不变,例如:return 10;
如果return是一个引用数据类型的值,当前实例会被自己返回的值替换,例如:ruturn {name:"张三"}
检测某个实例是否属于这个类 instanceof;
zhangsan instancaof CreateJs->true
in:检测某一个属性是否属于这个对象 attr in object,不管是私有属性还是共有属性,只要存在,用in检测都是true
hasOwnProperty:用来检测某一个属性是否为这个对象的私有属性,这个方法只能检测私有属性 obj.hasOwnProperty(attr);
function CreateJs(name,age) {
this.name = name;
this.age = age;
this.writeJs = function() {
console.log('my is '+ this.name +' can write js');
}
}
var zhangsan = new CreateJs('张三','18');
zhangsan.writeJs();
var lisi = new CreateJs('李四','20');
lisi.writeJs();
zhangsan.writeJs === lisi.writeJs//false
8、原型链模式
每一个函数数据类型(普通函数、类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值;
prototype上浏览器天生给它加了个属性constructor(构造函数),属性值是当前函数(类)本身;
每一对象数据类型(普通对象、实例、prototype..)天生自带一个属性__proto__,属性值是当前实例所属类的prototype(原型)
Object是Js中所有对象的基类,Object.prototype上没有__proto__这个属性
function CreateJs(name,age) {
this.name = name;
this.age = age;
}
CreateJs.prototype.writeJs = function() {
console.log('my is '+ this.name +' can write js');
}
var zhangsan = new CreateJs('张三','18');
zhangsan.writeJs();
var lisi = new CreateJs('李四','20');
lisi.writeJs();
zhangsan.writeJs === lisi.writeJs//true
通过对象名.属性名获取属性值的时候,首先在对象的私有属性找,如果私有属性存在,则获取的是私有属性值;
如果私有没有,则通过__proto__找到所属类的原型,原型上存在的话获取的是公有的属性值;
如果原型上也没有,则继续通过原型上的__proto__继续向上查找,一直找到Object.protoype为止
9、call、apply、bind使用
call、apply的区别
对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
实例:
//1、数组之间追加
var array1 = [12 , "foo" , {name "Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
/* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
//2、获取数组中的最大值最小值(数组中本身没有max方法)
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers), //458
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
//3、检验数据类型
Object.prototype.toString.call(obj) === '[object Array]' ;
//4、类数组转数组
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
bind
bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数
var bar = function(){
console.log(this.x);
}
var foo = {
x:3
}
bar(); // undefined
//bind是先绑定this和参数,不执行本身函数
//call和apply是绑定this和参数后立即执行本身函数
var func = bar.bind(foo);
func(); // 3
10、计算数组中最大值
<!--1、排序法-->
var arr = [12,43,2323,455,23,5];
arr.sort(function(x,y) {
return x-y;
})
var max = arr[arr.length-1];
<!--2、假设法-->
var arr1 = [123,34,54,23,56,1];
var max = arr1[0];
for(var i = 0;i < arr1.length;i++ ) {
var cur = arr1[i];
if(cur>max) {
max = cur[i];
}
}
<!--3、Math.max + apply法-->
var arr2 = [23,43,123,341,3233];
var max = Math.max.apply(null,arr2);
<!--4、Math.max + eval法-->
var arr3 = [567,23,42,45,1,98];
var max = eval("Math.max("+arrr.toString()+")");
11、数组求平均数
<!--1、类数组转数组-->
function avgFn() {
var arr = Array.prototype.slice.apply(arguments);
arr.sort(function(x,y) {
return x - y;
})
arr.shift()
arr.pop();
var arg =(eval(arr.join("+"))/arr.length).toFixed(2)
return arg;
}
avgFn(68,97,97,91,99.1,89.5,98.23)
<!--2、借助于call-->
function avgFn() {
var arr = [].sort.call(arguments,function(x,y) {
return x-y;
});
[].shift.call(arguments);
[].pop.call(arguments);
return (eval([].join.call(arguments,"+"))/arguments.length).toFixed(2);
}
avgFn(68,97,97,91,99.1,89.5,98.23)
12、sort的深入
arr.sort();只能默认排序10以内的数字和26字母排序
var arr = [1,3,4,65,23,32,43,567];
arr.sort();//log->[1, 23, 3, 32, 4, 43, 567, 65]
var arr1 = [2,5,6,4,9,1.2,0.9];
arr1.sort();//log->[0.9, 1.2, 2, 4, 5, 6, 9]
var arr2 = ['a','y','b','t','p','c'];
arr2.sort();//log->["a", "b", "c", "p", "t", "y"]
arr.sort(function(){})传递参数
<!--升序-->
var arr = [1,3,4,65,23,32,43,567];
arr.sort();
/**
*执行中a和b的值
*a b
*1 3
*3 4
*4 65
*65 23
*4 23
*65 32
*23 32
*65 43
*32 43
*65 567
*a - b > 0 a和b调换位置
*a - b <= 0 位置不变
*/
arr.sort(function(a,b){
return a - b;
})
<!--降序-->
arr.sort(function(a,b){
return b - a;
})
利用localeCompare字母排序
localeCompare第一个字符串字母按照26个字母顺序排序,如果第一个字母相同会按照第二个字母排序以此类推,汉字会先转换成拼音再排序,如果汉字同音会按照汉字unicode顺序排
'a'.localeCompare('b')
//-1
'c'.localeCompare('b')
//1
var arr = [{name:"wangwu",age:17},{name:"lisi",age:17},{name:"dahuang",age:21}];
arr.sort(function(a,b){
return a.name.localeCompare(b.name);
})
//[{name:"dahuang",age:21},{name:"lisi",age:17},{name:"wangwu",age:17}]
var arr = [{name:"小吕",age:17},{name:"老王",age:17},{name:"大黄",age:21}];
arr.sort(function(a,b){
return a.name.localeCompare(b.name);
})
//[{name:"大黄",age:21},{name:"老王",age:17},{name:"小吕",age:17}]
13、DOM回流(重排 reflow)、DOM重绘、DOM映射
DOM回流:DOM树渲染完毕以后,只要页面中的HTML结构发生变化(增加删除元素、位置发生变化),浏览器都要重新计算一遍最新的DOM结构,重新对当前页面进行渲染;
DOM重绘:DOM树位置不发生变化,如元素的颜色背景发生变化,会只针对这个元素渲染,不渲染整个页面
DOM映射:页面中的标签和Js中获取到的元素对象(元素集合)是紧紧绑定在一起的,页面中HTML结构改变了,Js不需要重新获取,集合里面的内容也会跟着自动改变
14、js中数据绑定的方法
1、动态创建节点方式
var oUl = document.getElementById('ul');
var oLi = document.createElement('li');
oLi.innerHTML = "hello world";
oUl.appendChild(oLi);
2、字符串拼接的方式
var oUl = document.getElementById('ul');
var str = '';
for(var i = 0;i < arr.length; i++) {
str += "<li>";
str += "hello" + arr[i];
str += "</li>"
}
oUl.innerHTML += str;
3、文档碎片方式
var oUl = document.getElementById('ul');
var frg = document.createDocumentFragment();//创建一个文档碎片
var oLi = document.createElement('li');
oLi.innerHTML = "hello world";
frg.appendChild(oLi);
oUl.appendChild(frg);
frg = null;//手动清空碎片
15、正则
什么是正则
它是一个规则,用来处理字符串的规则。
正则的创建
1. 字面量创建
var reg = /\d/;
2. 实例创建
var reg = new RegExp("\d");
元字符
每一个正则表达式都是由元字符和 修饰符组成
具有特殊意义的元字符
\ :转义后面字符所代表的含义 ^ :以某一个元字符开始 $ :以某一个元字符结束 \n :匹配一个换行符 . :除了\n以外的任意字符 () :分组 x|y :x或y中的一个 [xyz] :x或者y或者z中的任何一个字符 [a-z] :a-z之间的任意字符 [^a-z]:除了a-z之间的任何字符 \d :一个0-9之间的任何数字 \b :匹配一个边界符 \w :数字字母下划线中的任意一个字符 \s:匹配一个空白字符 空格、制表符、换页符...代表出现次数的量词元字符
\* :出现0到多次 \+ :出现1到多次 ? :出现0到1次 {n} :出现n次 {n,} :出现n到多次 {n,m} :出现n到m次
()分组的作用
改变x|y的优先级
var reg = /^18|19$/;
//18、19、181、189、119、819、1819....true
var reg = /^(18|19)$/;
//18、19true
分组引用
var reg = /^(\w)\1(\w)\2$/;
reg.test("aabb")//true
reg.test("abcd")//false
//\1代表和第一个分组出现一模一样的内容
//\2代表和第二个分组出现一模一样的内容
//去除重复的字符
var str = 'aaaabbbbccccddddddssss440000008888';
str.replace(/(\w)\1+/g,"$1");
//"abcds408"
分组捕获
正则在捕获的时候,不仅仅把大正则匹配到,而且还可以把小分组的内容捕获到
var reg = /^(\d{2})(\d{4})(\d{4})(\d{4})\d{2}(\d{1})[\d|X]$/
var arr = reg.exec('340604198802112411');
//["340604198802112411", "34", "0604", "1988", "0211", "1"]
//340604198802112411大正则匹配的内容
//"34", "0604", "1988", "0211", "1"小分组匹配
[](1)、在中括号中出现的所有的字符都是代表本身意义的字符(没有特殊的含义)(2)、中括号不识别两位数
var reg = /^[.]$/;
reg.test('1');//false
reg.test('.');//true
var reg = /[12]/;
//1||2 不是12
var reg = /^[21-57]$/;
//2||1-5||7中的任意一个
//年龄介于[16-58]
var reg = /^(1[6-9]|[2-4]\d|5[0-8])$/;
//简单的验证邮箱
var reg = /^[\w.-]+@[\da-zA-Z]+(\.[a-zA-Z]{2,4}){1,2}$/;
//中国标准真实姓名2-4位汉字
var reg = /^[\u4e00-\u9fa5]{2,4}$/;
//身份证号码
//340604198802112411
//34(省)0604(市区县)19880211(出身年月)24(没用)1(奇数男、偶数女)1(0-9||X)
var reg = /^(\d{2})(\d{4})(\d{4})(\d{4})\d{2}(\d{1})[\d|X]$/;
正则的捕获exec
捕获的内容是个数组
var reg = /\d+/;
reg.exec('ducen23niubi21');
//0:"23",index:5,input:"ducen23niubi"
正则捕获的特点
懒惰型:每一次执行exec只捕获第一个匹配的内容,在不进行任何处理的情况下,在执行多次捕获,捕获的还是第一个匹配的值
var reg = /\d+/
reg.lastIndex//0
res = reg.exec('ducen23niubi12')//["23"]
reg.lastIndex//0
res = reg.exec('ducen23niubi12')//["23"]
//解决懒惰性,在正则后加修饰符g
var reg = /\d+/g
reg.lastIndex//0
res = reg.exec('ducen23niubi12')//["23"]
reg.lastIndex//7
res = reg.exec('ducen23niubi12')//["12"]
reg.lastIndex//14
res = reg.exec('ducen23niubi12')//null
贪婪性:正则每一次捕获都是按照匹配最长的结果
var reg = /\d+/;
res = reg.exec('ducen2017niubi12');
//捕获到的是["2017"]而不是2
//解决正则的贪婪性(在量词元字符后面添加一个?)
var reg = /\d+?/;
res = reg.exec('ducen2017niubi12');
//捕获的是["2"];
++?在正则中有很多作用,放在一个不同的元字符后面代表出现0-1次 /d?/(数字出现一次或不出现);放在一个量词的元字符后面是取消捕获的贪婪性++
正则的修饰符(g、i、m)
var reg = /\d/gim
//g:全局匹配
//i:忽略大小写匹配
//m:多行匹配
var reg = /\d+/g
var arr = []
res = reg.exec('ducen23niubi12');
while(res) {
arr.push(res[0]);
res = reg.exec('ducen23niubi12')
}
arr//["23", "12"]
字符串中的match方法
把所有和正则匹配的字符都获取到
var reg = /\d+/g
var arr = 'ducen23niubi12'.match(reg);
arr//["23", "12"]
虽然在当前的情况下match比我们的exec更加简便一些,但是match中存在一些自己处理不了的问题:在分组捕获的情况下,match只能捕获大正则匹配的内容,而对小正则捕获的内容无法获取
字符串中的replace方法
var str = "ducen2015ducen2016";
str.replace('ducen','huming');
//"huming2015ducen2016"
str.replace(/ducen/g,function(arg){
console.log(arguments);
<!--执行2次
["ducen", 0, "ducen2015ducen2016"]
["ducen", 9, "ducen2015ducen2016"]
-->
return 'huming';
})
//"huming2015huming2016"
str.replace(/ducen(\d+)/g,function(arg){
console.log(arguments)
console.log(arguments[1])//2015 2016 获取小正则
})
<!--["ducen2015", "2015", 0, "ducen2015ducen2016"]-->
<!--["ducen2016", "2016", 9, "ducen2015ducen2016"]-->
replace中的匿名函数执行次数,取决于正则捕获的次数
每一次执行匿名函数里面传递的参数值arguments和我们自己通过exec捕获到的结果是非常类似的(即使正则有分组,我们也可以通过arguments获取)
return:你返回的结果是啥,就相当于把当前大正则捕获的内容替换成返回的内容
var arr = ['零','壹','贰','叄','肆','伍','陆','柒','捌','玖']
var s = "20170401";
s.replace(/\d/g,function(){
return arr[arguments[0]]
})
<!--"贰零壹柒零肆零壹"-->
<!--千位分割符-->
//1
"2343289".replace(/^(\d{1,3})((?:\d{3})+)$/,function(){
return arguments[1]+"," + arguments[2].replace(/(\d){3}(?!$)/g,function(){
<!--(?=$)、(?!$)正向预测、负向预测 -->
return arguments[0]+ ","
})
})
//2
var str = '2343289';
str.replace(/(\d)(?!$)/g,function(res,i){
if((str.length-i-1)%3==0) {
return res+ ","
}else{
return res;
}
})
//3
var str = '2343289';
str = str.split('').reverse().join('');
str = str.replace(/(\d{3})/g,'$1,');
str.split('').reverse().join('');
<!--结果:2,343,289-->
正则中?的用法
在量词后面代表0到1次(?)
var reg = /^(\+|-)?\d+(\.\d+)?$/;
匹配不捕获(?:)
var reg = /^(?:\+|-)?\d+(\.\d+)?$/
取消正则贪婪捕获,把?放在量字后面(?)
var reg = /\d+/;
res = reg.exec('ducen2017niubi12');
//捕获到的是["2017"]而不是2
//解决正则的贪婪性(在量词元字符后面添加一个?)
var reg = /\d+?/;
res = reg.exec('ducen2017niubi12');
//捕获的是["2"];
正向预查、负向预查(条件)
(?!n)->匹配任何其后紧接指定字符串 n 的字符串。
(?=n)->匹配任何其后没有紧接指定字符串 n 的字符串。
javascript基础总结(二)——异步编程情况
异步:规定要做一件事,不是立马执行这件事,需要等一定的时间,这样的话,我们不会等着它执行,而是继续执行下面的操作,只有将下面的事情处理完了,才会返回头处理之前的事情;如果下面的事情并没有处理完成,不管之前的事情有没有到时间,都踏踏实实的给我等着;
1、定时器都是异步编程
var n = 0;
setTimeout(function(){
n++;
console.log(n);//2->1
},1000)
console.log(n);//1->0
var n = 0;
setTimeout(function(){
n++;
console.log(n);//2->1
},0)
console.log(n);//1->0
所有的定时器都会放在任务队列池中,时间短的放在前面;
var n = 0;
setTimeout(function(){
n++;
console.log('1:'+n);//2:2
},2000)
setTimeout(function(){
n++;
console.log('2:'+n);//2:1
},1000)
console.log(n);//1->0
如果定时器时间一样会从上向下执行;
var n =0;
setTimeout(function(){
n++;
console.log('1:'+n);
},2000)
var timer= setInterval(function(){
n++;
console.log('2:'+n);
if(n==5){
clearInterval(timer);
}
},1000)
//执行顺序
//2:1
//1:2
//2:3
//2:4
//2:5
2、所有的事件绑定都是异步编程
for 循环执行完成后才会走绑定事件
for(var i = 0;i < oLis[i].length;i++) {
oLis[i].onclick = funciton() {
changeEvent(i);//i永远是最后一个
}
}
3、ajax异步读取数据时
4、通过回调函数实现异步
通过ajax和setTimeout辅助实现
//jquery中的$.get方法等...
$.get('url', function(p) {
//some
});
//借助于setTimeout、setInterval
function f1(callback){
setTimeout(function () {
// f1的任务代码
callback(); // step3
}, 1000);
}
f1();//setep1
console.log('x');//step2
如果该调用需要耗费很多时间,执行队列就会因等待而阻塞,采用回调函数,执行队列继续进行,等到调用结束,通过调用回调函数的方式,通知执行队列,处理执行结果.
javascript基础总结(三)——盒子模型
1、js中的盒子模型
通过js中提供一系列的方法和属性获取页面中元素的样式信息值;
2、client系列
内容的宽高:是给元素定义的width/height这两个样式。如果没有设置height值,容器的高度会根据里面内容自己适应,这样获取的值就是真实的内容的高;如果设置固定的高度,不管内容是多少,内容的高度指的都是设定的这个值;
真实内容的宽高:如果设置的height是200px,如果内容有溢出,那么真实内容的高度是把溢出内容的高度也要加起来;
clientHeight || clientWidth
//内容的宽度/高度+左右/上下填充(padding)
clientLeft || clientTop
//左边框/上边框的高度
3、offset系列
offsetHeight || offsetWidth
//clientHeight/clientWidth + 左右/上下边框(和内容是否溢出没有关系)
offsetParent
//当前元素的父级参照物
offsetLeft || offsetTop
//当前元素的外边框距离父级参照物的内边框的偏移量
计算元素距离body的上部和左部的距离
function offset(el) {
var oLeft = el.offsetLeft,
oTop = el.offsetTop,
oParent = el.offsetParent;
while(oParent) {
//ie8下不计算边框
if (navigator.userAgent.indexOf('MSIE 8.0') === -1) {
oLeft += oParent.clientLeft;
oTop += oParent.clientTop;
}
oLeft += oParent.offsetLeft;
oTop += oParent.offsetTop;
oParent = oParent.offsetParent;
}
return {'left': oLeft,'top': oTop}
}
offset(box2);//{left: 239, top: 218}
注:在标准的IE8浏览器中,我们使用offsetLeft/offsetTop其实是把父级参照物的边框已经算在内,在IE8浏览器下就不需要单独加边框了;
4、scroll系列
scrollHeight || scrollWidth
//真实内容的高度/宽度(包含溢出)+ 左填充/上填充
//注:获取到的结果都是约等于的值,因为同一个浏览器是否设置overflow=hidden对于最终的结果是有影响的;在不同的浏览器中我们获取的结果也是不同的。
scrollTop || scrollLeft
//滚动条卷去的高度
5、操作浏览器本身盒子模型信息
clientWidth/clientHeight是当前浏览器可视窗口的宽度和高度
scrollWidth/scrollHeight是当前页面的真实宽度和高度(所有屏的高度和宽度的和:是一个约等于值)
要兼容浏览器获取浏览器盒子模型信息我们需要这样写
document.documentElement[attr] || document.body[attr];
//documentElement在前body在后
//获取
document.documentElement.clientWidth || document.body.clientWidth;
//设置
document.documentElement.scrollTop = 0;
document.body.scrollTop = 0;
<!--注:都需要写两个-->
6、获取浏览器盒子模型信息的兼容方法
function win(attr,value) {
//获取值
if(typeof value === "undefined") {
return document.documentElement[attr] || document.body[attr];
}
document.documentElement[attr] = value;
document.body[attr] = value;
}
win('clientWidth');
win('scrollTop',0);
es6中的箭头函数
箭头函数(arrow function)
箭头函数相当于一个匿名函数
x => x * x;
//同等于下面的匿名函数
//x - >传参
//x * x -> return
function(x) {
return x * x;
}
如果箭头函数含表达式就必须加{...} 和 return
x => {
if(x>10) {
return x + x;
}else {
return x * x;
}
}
如果箭头函数不是一个参数,参数就必须加()->(x,y)
//两个参数
(x,y) => x * y
//没有参数
() = > 1 + 4
//可变参数
var fn = (x,...rest) => {
for(var i = 0;i < rest.length; i++) {
x += rest[i]
}
return x;
}
//rest是个Array [4,5]
fn(1,4,5);//10
如果return的值是一个对象需要加()进行区分,防止冲突
var fn = x => { foo: x }
fn(3);//undefined
var fn = x => ({ foo: x })
fn(3);//Object {foo: 3}
箭头函数中的this由上下文决定
//es6 =>写法
var obj = {
age: 18,
fnc : function (x) {
var fn = x => this.age + x;//this->obj
return fn(x);
}
}
obj.fnc(5)//23
//es5写法
//错误的写法
var obj = {
age: 18,
fnc : function (x) {
var fn = function (x) {
return this.age + x;//this->window
}
return fn(x);
}
}
obj.fnc(5)//NaN
//正确的写法
var obj = {
age: 18,
fnc : function (x) {
var that = this;//this->obj
var fn = function (x) {
return that.age + x;
}
return fn(x);
}
}
obj.fnc(5)//23
如果用call()或者apply()调用箭头函数时,无法对this进行绑定(传入的第一个参数被忽略):
var obj = {
age: 18,
fnc : function (x) {
var fn = x => this.age + x;//this->obj
return fn.call({age:20},x);//用call无法改变this的指向,箭头函数中的age依然等于18
}
}
obj.fnc(5)//23
JavaScript面试(-------------------------------------------)的更多相关文章
- JavaScript面试时候的坑洼沟洄——表达式与运算符
上篇博客JavaScript面试时候的坑洼沟洄--数据类型总结了一下JavaScript数据类型几转换的相关知识,很多朋友可能和我一样,买了书后对数据类型啊.运算符啊.语句啊都是扫两眼或直接略过的,自 ...
- JavaScript面试时候的坑洼沟洄——数据类型
前些日子写了篇关于最近找工作的一些感受的博客 找工作的一些感悟--前端小菜的成长,没想到得到了很多园友的共鸣,得到了很多鼓励,也有园友希望我分享一些笔试.面试的经验.我觉得分享一些笔试题没太多价值,对 ...
- 5个经典的JavaScript面试基础问题
JavaScript程序员在IT领域中的需求量非常巨大.如果你非常精通JavaScript,你会有很多换工作.涨薪水的机会.但是在一家公司录用你之前,你必须顺利通过面试,证明你的技能.在本文中,我将向 ...
- JavaScript面试的完美指南(开发者视角)
为了说明 JS 面试的复杂性,首先,请尝试给出以下结果: onsole.log(2.0 == "2" == new Boolean(true) == "1") ...
- 快速掌握JavaScript面试基础知识(三)
译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...
- 快速掌握JavaScript面试基础知识(二)
译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...
- 26个精选的JavaScript面试问题
译者按: 从各个平台精选整理出26道由浅入深的题目助你面试 原文: Top 26 JavaScript Interview Questions I Wish I Knew 译者: Fundebug 为 ...
- 攻破javascript面试的完美指南【译】
攻破javascript面试的完美指南(开发者视角) 0. 前言 本文适合有一定js基础的前端开发人员阅读.原文是我google时无意发现的, 被一些知识点清晰的解析所打动, 决定翻译并记录下来.这个 ...
- JavaScript面试问题:事件委托和this
JavaScript不仅门槛低,而且是一门有趣.功能强大和非常重要的语言.各行各业的人发现自己最混乱的选择是JavaSscript编程语言.由 于有着各种各样的背景,所以不是每个人都对 ...
- 25个最基本的JavaScript面试问题及答案
1.使用 typeof bar === "object" 来确定 bar 是否是对象的潜在陷阱是什么?如何避免这个陷阱? 尽管 typeof bar === "objec ...
随机推荐
- .htaccess技巧: URL重写(Rewrite)与重定向(Redirect)
URL重定向是.htaccess的重头戏,它可以将长地址转为短地址.将动态地址转为静态地址.重定向丢失的页面.防止盗链.实现自动语言转换等.笔者觉得难点是在正则表达式的运用和理解上. 实现所有这些神奇 ...
- javascript sleep方法
function sleep(numberMillis) { var now = new Date(); var exitTime = now.getTime() + numberMi ...
- HDU 1285 确定比赛名次(拓扑排序模板)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1285 题目大意:有N个比赛队(1<=N<=500),编号依次为1,2,3,....,N进行 ...
- Hadoop案例(四)倒排索引(多job串联)与全局计数器
一. 倒排索引(多job串联) 1. 需求分析 有大量的文本(文档.网页),需要建立搜索索引 xyg pingping xyg ss xyg ss a.txt xyg pingping xyg pin ...
- 【C#】线程问题
多线程编程对很多程序员来说并不容易,在启动访问相同数据的多个线程时,会间歇性地遇到难以发现的问题.如果使用任务.并行LINQ或Parallel类,也会遇到这些问题.为了避免这一系列问题,开发程序中必须 ...
- 微信公共服务平台开发(.Net的实现)1 认证“成为开发者”
http://www.cnblogs.com/freeliver54/p/3725979.html http://www.it165.net/pro/html/201402/9459.html 这些代 ...
- CodeForces 805A Fake NP
直觉. 一段区间中,肯定是$2$的倍数最多,因为区间长度除以$2$得到的数字最大.但只有$1$个数字的时候需要特判. #include <cstdio> #include <cmat ...
- 洛谷P2730 魔板 [广搜,字符串,STL]
题目传送门 魔板 题目背景 在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板.这是一张有8个大小相同的格子的魔板: 1 2 3 4 8 7 6 5 题目描述 我们知道魔板的每一个方格都有 ...
- 洛谷P2571 [SCOI2010]传送带 [三分]
题目传送门 传送带 题目描述 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.lxhgww在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移 ...
- 初拾Java(问题三:乱码问题)
年后的工作不是那么的忙,最近也开始思考自己以后的路该怎么走,在迷茫的时候,还是坚持学习点儿东西吧. 接着之前的JSP,这次是要尝试着用request.getParameter(ParameterNam ...