JS面试

JS中的THIS汇总
THIS:当前方法执行的主体(谁执行的这个方法,那么THIS就是谁,因此THIS和当前方法在哪建立的或者在哪执行的都没有必然的关系)

1.给元素的某个事件绑定方法,方法中的THIS都是当前操做的元素自己
document.body.onclick = function () {
=>this:body
};

2.函数执行,看函数前面是否有点,有的话,点前面是谁THIS就是谁,没有点,THIS是WINDOW(在JS的严格模式下,没有点THIS是UNDEFINED)
let fn = function () {
console.log(this.name);
};
let obj = {
name: '哈哈',
fn: fn
};
fn();//=>this:window
obj.fn();//=>this:obj

3.构造函数执行,方法中的this通常都是当前类的实例
let Fn = function () {
this.x = 100;//=>this:f
};
let f = new Fn;

4.箭头函数中没有本身的THIS,THIS是上下文中的THIS
let obj = {
fn: function () {
this:obj
setTimeout(() => {
this:obj
}, 1000);
}
};
obj.fn();

5.在小括号表达式中,会影响THIS的指向
let obj = {
fn: function () {
console.log(this);
}
};
obj.fn();//=>this:obj
;(12, obj.fn)();//=>this:window

6.使用call/apply/bind能够改变this指向
fn.call(obj);//=>this:obj
fn.call(12);//=>this:12
fn.call();//=>this:window 非严格模式下call/apply/bind第一个参数不写或者写null和undefined,this都是window,严格模式下写谁this就是谁,不写是undefined

1. 写出你所熟知的ES6新语法,说出它们和ES5的区别!

Es6中语法让JS更加的严谨
  • let和const 与ES5的Var区别(let 不存在变量提高机制,不容许重复声明,在全局做用域中基于Let声明的变量不是Window的一个属性和他没有关系 ,Typeof未被声明,let会造成块级做用域(相似于私有做用域,大部分的大括号都会造成块做用域))
  • 解构赋值
  • “...”拓展、剩余、展开运算符
  • 箭头函数(与普通函数区别:没有arguments,可是能够基于...arg获取实参集合,没有本身的this,箭头函数中的this是上下文中的this)
  • promise(async与awit)异步处理
  • 对于插件通常用到class类来封装(ES中建立类的)
  • interator(for of 循环)
  • ES6中字符串模板
  • map/set

2. 请说出你对 “重排和重绘读写分离” 的理解! 重排(就是回流)
思路:1.首先说出什么是重排和重绘
2.突出他们耗性能
3.突出本身写项目的时候重点注意了这些事情,以及本身的解决方案(说一下解决原理 )
浏览器渲染一个页面的时候是按照“先建立DOM树-》在加载CSS-》生成渲染树render tree->把渲染树交给浏览器进行绘制”,若是后期咱们修改了元素的样式(可是没有修改大小和位置),浏览器会把当前元素从新生成渲染 树,这个机制是重绘,介是一旦元素的位置或大小等发生改变,浏览器就要从DOM权从新计算渲染,这个机制是回流(重排),不管是重排仍是重绘都很是 的耗性能。
在个人之前的项目中,我特地的重视了这个问题,尽可能减小操做Dom引起的回流和重绘问题,经常使用的解决方案:
1. 须要动态向页面追加元素的时候,基于文档碎片或者先把须要增长的全部元素拼接成字符串,最后统一进行增长
  1. 读写分离:把统一修改样式都放在一块儿执行,新版浏览器都有一个本身检测的机制,若是发现下面紧挨着的操做也是修改元素的样式,会把全部修改的事先存起来,直到遇到非修改样式的操做,会把以前存储的统一执行,引起一次回流和重绘。
  2. 固然还有一些其余的方法,这些是最常注意的,我认为减小DOM的回流重绘是很是 重要的性能优化手段之一

3. 写出下面代码运行的结果
var str='abc123',
num=parseFloat(str);
if(num===NaN){
alert(NaN);
}else if(num===123){
alert(123);
}else if(typeof num==='number'){
alert('number');
}else{
alert('str');
}

-->number
--------------------------


4. 写出代码执行的结果
var a='abc'+123+456;
alert(a);

-->'abc123456'
--------------------------
var b='456'-'123';
alert(b);

-->'333'
--------------------------
var c=1,
d='1';
var f=c>d?(c<d?c:d):(c==d?c:d);
alert(f);

-->'1'
--------------------------

5. 用户昵称规定只能是“数字、大小写字母”组成,并且不能少于2位,也不能超过20位,写个正则匹配这个需求

let reg=/^[0-9a-zA-Z]{2,20}$/
--------------------------
6. 谈谈你对面向对象的理解!
【JS自己是基于面向对象的】
JS自己是基于面向对象的编程思想开发出来的语言,咱们学习JS就是在学习JS中的类和实例,例如:数组是Array的实例、对象是Object的实例、函数是Function的实例...在这些内置类的原型上有不少的公共的属性和方法,这些方法能够被实例调用,咱们学习JS就是学习这些方法。。。
【面向对象其实项目的应用】
平时的业务逻辑开发,我没有刻章使用类的方式来作,只有在一些组件或者插件封装的时候才会基于构造函数和原型链使用类和实例完成,例如:我以前封装过一些TAB页卡、轮播图、模态框、表单验证等插件,就是这样处理的(我以前看了一些类库和插件的源码,也都是基于面向对象封装的)
【面向对象中的一些语法和特色】
所谓面向对象就是基于Class或者function建立一个类,执行的时候new执行建立一个实例,这样实例就能够调取类上提供的方法, 想要基于面向对象进行插件封装,必须掌握关于类的继承、封装和多态,封装就是提供公共的方法、JS中没有严格意义的多态,不能进行方法的重写,经常使用的继承方式有不少,例如:原型继承、call继承、寄生组合继承、es6中的继承等,有些方式会存在一些问题,我项目中后来都是基于Class中的Extend实现继承的

7. 写出代码运行结果
var point={
x:10,
y:20,
moveTo:function(x,y){
var moveX=function(x){ this.x=x; }
var moveY=function(y){ this.y=y; }
moveX(x);
moveY(y);
}
};
point.moveTo(100,200);
console.log(point.x,point.y);

-->10 20
--------------------------
8. 分析代码写结果
function fun(){
this.a=10;
this.b=function(){
alert(this.a);
}
}
fun.prototype={
b:function(){
this.a=20;
alert(this.a);
},
c:function(){
this.a=30;
alert(this.a)
}
}
var my_fun=new fun();
my_fun.b();
my_fun.c();

-->10 30
--------------------------
9. 分析代码写结果
var n=2;
function a(){
var n=3;
function b(m){
alert(++n+m);
}
b(4);
return b;
}
var c=a(5);
c(6);
alert(n);

-->8 11 2
--------------------------
10. 谈一下你对做用域链和原型链的理解
做用域链-》函数执行会造成一个私有的做用域,形参和在当前私有做用域中声明的变量都是私有变量,当前的私有做用域有自我保护机制,私有变量和外界是没有关系的,可是若是私有做用域中遇到一个非私有的变量,则向它的上级做用域找,若是还不是上级做用域私有的,则继续向上查找,一直找到window为止。这种变量一层向上查找的机制就是做用域链机制。
原型链--》它也是一种查找机制,实例首先在本身的私有属性中进行属性的查找,若是不是私有属性,基于__proto__向所属性的原型上进行查找,若是在__proto__找不到,则继续基于向上查找,一直打到object.prototype为止,例如Obj.hasOwnProperty()这里调取hasOwnProperty()的这个属性就是找到Obj.hasOwnProperty()才找到的

11. 实现 一个$attr(domId,name,value)遍历id是domId的,内部属性为name且值为value的元素?

let $attr = (domID, name, value) => {
//1.先获取当前页面中全部的标签
let tagList = document.getElementsByTagName('*');

//2.在获取的全部标签中按照ID/NAME/VALUE进行筛选(数组内置方法:filter)
tagList = [].slice.call(tagList);//=>把类数组转换为数组
//=> tagList=[...tagList] 基于ES6中的展开运算符完成,让TAG-LIST等于一个数组,数组中的每一项是把以前的类数组展开后获得的
tagList = tagList.filter(item => {
//=>item.name:只有表单元素这样才能够获取到值,普通元素须要基于getAttribute获取值
// return item.id === domID && item.getAttribute('name') === name && (item.innerHTML === value || item.value === value);//=>传统标签获取里面的内容不是基于VALUE属性,而是基于INNER-HTML/INNER-TEXT属性完成的

return item.id === domID && item.getAttribute(name) === value;
});
return tagList;
};
console.log($attr('hobbyBox', 'hobby', 'music'));

// let ary = [12, 23, 34, 25, 36, 47];
// ary = ary.filter((item, index) => {
// return item > 20 && item < 40;//=>返回的结果是TRUE或者FALSE,返回的是TRUE会把这一项存放到新数组中(基于FILTER不会修改原有的数组,会把遍历后符合条件的放到新数组中)
// });

//=>扩展:获取当前页面中全部ID重复的元素

12. 实现数组去重你都有哪些办法?

//=>数组去重(不改变原有数组)

//1.对象键值对处理(推荐)
Array.prototype.myUnique = function () {
=>this:ary //咱们须要操做的数组,若是不想改变原有的数组,咱们须要把要操做的数组克隆一份如出一辙的处理,处理的都是克隆的这个数组
let _this = [...this],
obj = {};
for (let i = 0; i < _this.length; i++) {
let item = _this[i];
if (typeof obj[item] !== 'undefined') {
//=>当前迭代的这一项在数组中已经存在,咱们把这一项在数组中干掉
// _this.splice(i, 1); [后面项移位,消耗性能]
_this[i] = _this[_this.length - 1];
_this.length--;
i--;
continue;
}
obj[item] = true;
}
obj = null;
return _this;
};

//=>双循环(不推荐)
Array.prototype.myUnique = function () {
let _this = [...this];
for (let i = 0; i < _this.length; i++) {
let item = _this[i];
//=>每一次迭代到ITEM后,都拿其后面的内容和它进行比较(出现和当前项相同的,咱们就在数组中把其干掉)
for (let j = i + 1; j < _this.length; j++) {
if (item === _this[j]) {
//=>删除索引J这一项
_this[j] = _this[_this.length - 1];
_this.length--;
j--;
} } }
return _this;
};

//=>indexOf:获取当前项在数组中第一次出现位置的索引,也能判断是否存在这一项(不存在获取的索引是-1),这个方法是不兼容IE6~8的
Array.prototype.myUnique = function () {
let _this = [...this];
//=>依次迭代数组中的每一项,验证当前项在数组中是否存在(不是和整个数组比较是否存在,而是和当前项的后面项比较是否存在=>相似于双FOR),存在把当前项干掉
for (let i = 0; i < _this.length; i++) {
let item = _this[i],
nextAry = _this.slice(i + 1);
if (nextAry.indexOf(item) > -1) {
_this[i] = _this[_this.length - 1];
_this.length--;
i--;
} }
return _this;
};

//=>排序后相邻去除法
//先把数组进行排序,验证当前项和后一项是否相同,若是不相同,说明没有重复,咱们把着于相提取出来保存便可
Array.prototype.myUnique = function () {
let _this = [],
ary = this.slice(0).sort((a, b) => a - b);
for (let i = 0; i < ary.length; i++) {
let item = ary[i],
next = ary[i + 1];
if (item !== next) {
_this.push(item);
}
}
return _this;
};

let ary = [1, 2, 3, 2, 3, 4, 3, 2, 2, 2, 2, 3, 4, 5, 6, 7, 4, 1, 3, 2];
let uniqueAry = ary.myUnique();
console.log(uniqueAry);


13. 说出你所掌握的算法
/*
* 经常使用的算法(算法是思惟模式)
* 递归(函数本身调用本身执行就是递归 (递归是基于条件判断的:由于咱们不能造成死递归,在某个条件下咱们须要结束递归操做)
* 去重
* 冒泡排序
* 插入排序
* 快速排序
* 时间复杂度
* 空间复杂度
* KMP
* ...
*/
///=>数组扁平化(多维数组=>一维数组)
let ary = [1, [2, [3, [4, 5]]], [6, 7, [8, 9, [11, 12]], 10]]; //=>[1,2,3,4,5,6]
let str = JSON.stringify(ary);
//=>第一种处理
console.log(str);//=>[1,[2,[3,[4,5]]],6]
ary = str.replace(/(\[|\])/g, '').split(',');
console.log(ary);
//=>第二种处理
str = str.replace(/(\[|\])/g, '');
str = '[' + str + ']';
ary = JSON.parse(str);
console.log(ary);

let result = [],
fn = function (ary) {
if (ary.length === 0) return;
for (let i = 0; i < ary.length; i++) {
let item = ary[i];
if (typeof item === 'object') {
fn(item);
} else {
result.push(item);
}}};
fn(ary);
console.log(result);

14. 写出你掌握的JS继承方式,项目中何时你用到了继承?

* 面向对象:类的继承封装和多态
* [封装] 把实现一个功能的JS代码进行封装,主要目的:“低耦合高内聚”

* [多态]
* 重载:方法名相同,参数的个数或者类型不一样,此时名字相同的方法叫作方法的重载(后台语言中的重载),JS中不存在重载的(=>根据传递参数的不一样执行不一样的方法)
* 重写:子类重写父类的方法
*
* [继承]
* 子类继承父类的属性和方法
* 1. 原型继承
* 2. call继承
* 3. 寄生组合继承
* 4. ES6中class类实现继承
* ...
//=>原型继承:让子类的原型指向父类的一个实例
方式:B.prototype=new A();A的实例自己具有父类A的私有属性和公有方法,子类B的原型指向它,那么子类B的实例就能够找到这些属性方法了,
和传统后台语言的继承不同,子类继承父类,并非把父类的属性方法克隆一份给子类的,JS中的原型继承是让子类和父类创建原型连接的机制,子类的实例调取父类原型上的方法,都是基于原型链的查找机制完成的
存在的问题:子类能够重写父类原型上的方法(重写)子类和父类还有关系的
B.prototype.__proto__.getX=null; 把父类A的原型上的getX重写为Null,A的其它实例也会受到影响


//=>CALL继承:把父类A作为普通函数执行,让A中的THIS变为B的实例,至关于给B的实例增长一些属性和方法(弊端:把父类A当作普通函数执行,和父类原型没啥关系了,仅仅是把A中的私有属性变为子类B实例的私有属性而已,A原型上的公有属性方法和B及它的实例没啥关系)

//=>寄生组合继承:A的私有变为B的私有,A的公有变为B的公有

//=>ES6中的类和继承(ES6中建立类是有本身标准语法的(这种语法建立出来的类只能NEW执行,不能当作普通函数执行))

15. JS中有一个insertBefore方法,目的是实现把新元素插入到指定元素以前,如今你实现一个 InsertAfter 方法,把新元素插入到指定元素以后!
function insertAfter(newEle,originEle){
//=>newEle:新插入的元素
//=>originEle:指定的老元素
let next=originEle.nextElementSibling(),
pra=originEle.parentNode;
if(next){
pra.insertBefore(newEle,next)
}else{
pra.appendchild(newEle)
}}

16. 英文字母汉字组成的字符串,用正则给英文单词先后加空格
let reg=/^$/

17. jQuery的原理,怎么扩展插件
JQ是一个JS类库,里面提供了不少的经常使用方法,有助于咱们快速开发,并且这些方法是兼容全部浏览器的(V2 / V3 不兼容低版本浏览器)
我以前在学习原生JS的时候,或多或少的看了一部分JQ源码,刚毕业的时候JQ用的比较多,可是最近两年一直都在用框架开发,JQ中经常使用的方法忘差很少了。以前看源码的时候,发现JQ就是一个类,而$()就是建立这个类的一个实例,这个实例是基于内置方法makeArray创造的类数组
JQ提供的方法有两部分,一部分是放到原型上的,供实例调取使用,一部分是放到对象上的,直接$.xxx调取使用,想要后期本身扩展方法(包括基于JQ写插件),均可以基于extend这个方法向JQ中扩展
JQ中提供了动画、事件、AJAX等经常使用的方法,我学习JQ源码的时候比较注重里面的一些封装和编程的思想,例如:发布订阅这种设计模式我就是依据JQ的$.Callbacks学习研究的,因此学习JQ给我带来了不少的好处...


18. 看代码,回答问题
for(var i = 0;i<5;i++){
setTimeout(function(){
console.log(i)
},1000);
}
这段代码输出什么?怎么才能输出01234?
19. 分析代码写结果
var a = {n:4};
var b = a;
b.x = a = {n: 10};
console.log(a.x);
console.log(b.x);
20. 你了解过闭包吗?

var fullName='language';
var obj={
fullName:'javascript',
prop:{
getFullName:function(){
return this.fullName;
}
}
};
console.log(obj.prop.getFullName());
var test=obj.prop.getFullName;
console.log(test());
22.
let a = 3,
b = 4;
function A(a) {
A = function (b) {
alert(a + (--b));
};
alert(++a);
}
A(5);
A(6);
23.
window.val = 1;
let json = {
val: 10,
dbl: function () {
this.val *= 2;
}
};
json.dbl();
let dbl = json.dbl;
dbl();
json.dbl.call(window);
alert(window.val + json.val);
24.
(function () {
let val = 1;
let json = {
val: 10,
dbl: function () {
val *= 2;
}
};
json.dbl();
alert(json.val + val);
})();
25.
let test = (function (i) {
return function () {
alert(i *= 2);
}
})(2);
test(5);
26.
let n = 2,
fn = () => {
this.n *= 3;
n++;
return m=>console.log((++n)+m);
};
var f = fn(4);
f(5);
fn(4)(5);
f(6);
console.log(n);
27. 忽略报错阻碍代码的执行
let Fn = function (x = 0, y = 0) {
this.x = x;
this.y = y;
this.getX = function () {
console.log(this.x);
}
};
Fn.prototype.getX = function () {
console.log(this.x);
};
let f1 = new Fn;
Fn.prototype = {
getY: function () {
console.log(this.y);
}
};
let f2 = new Fn(1, 2);
console.log(f1.constructor===f2.constructor);
f1.getX();
f1.getY();
f1.__proto__.getX();
f1.__proto__.getY();
f2.getX();
f2.getY();
f2.__proto__.getX();
f2.__proto__.getY();
28. 写出输出结果,说出缘由
let fn1=function(){alert(1)},
fn2=function(){alert(2)};
fn1.call(fn2);
fn1.call.call(fn2);
29. 以下一个字符串 “54389”,要求将字符串中的阿拉伯数字替换成咱们的中文大写数字”伍肆叁捌玖”,请使用正则的方式进行处理
let str='54389';
let ary=['零','壹','贰','叁','肆','伍','陆','染','捌','玖'];
str=str.replace('/\d/g',item=>{
//=>item =>arg[0] =>正则每一次捕获的内容 5/4/3/8/9
// //=>把捕获的数字作为索引,到ARY中找到对应的汉字,用找到的结果替换当前捕获的内容
return ary[item];
})

30. 在javascript对象上定义一个repeatify函数,这个函数接受一个整数参数,来明确子字符串须要重复几回,这个函数要求字符串重复指定的次数,好比:’abc’.repeatify(3);//”abcabcabc”

String.prototype.repeatify=function repeatify(n=1){
// =>this: 须要处理的字符串
let result='';
for(let i=0; i< n; i++){
result+=this;
}
return result;
}

31. var str='hello<img src="haha.png" alt="哈哈"/>world';正确匹配输出’hello[哈哈]world’
var str='hello<img src="haha.png" alt="哈哈"/>world';
let reg=/<img [^<>]*alt=(?:"|')(.*)(?:"|')\/>/g
str=str.replace(reg,(...arg)=>{
return `[&{arg[1]}]`;
})


32. 一个url 后面好多key-value 如localhost?key=val&key2=val2&key3=val3 封装一个函数 getParam(‘key’) 经过key得到相应等号后面的值.
let getParam=function (attr){
let str='localhost?key=val&key2=val2&key3=val3',
obj={},
reg=/([^?&=#]+)=([^?&=#]+)/g;
str.replace(reg,(...arg)=>{
let [,key,value]=arg;
obj[key]=value;
})
return obj[attr];
}
getParam(‘key’)

/*
NODE中提供一个URL.PARSE方法,这个方法能够把一个URL地址中的每一部分都捕获到,最后存储成为一个对象

let url = require('url');
console.log(url.parse(' www.zhufengpeixun.cn:80/stu/index.h…', true));
{
protocol: 'http:',
port: '80',
hostname: ' www.zhufengpeixun.cn',
hash: '#teatcher',
search: '?name=xxx&age=9',
query: { name: 'xxx', age: '9' },
pathname: '/stu/index.html'
}
*/

/*
let link = document.createElement('a');
link.href = str;
let {hash, hostname, pathname, protocol, search, port} = link;
//=>端口号:若是没有值,则使用默认端口(HTTP:80 HTTPS:443 FTP:21)
if (!port) {
switch (protocol) {
case 'https:':
port = 443;
break;
case 'ftp':
port = 21;
break;
default:
port = 80;
}
}
//=>QUERY
let query = {};
if (search) {//=>search:"?name=xxx&age=9"
search.replace(/([^?=&]+)=([^?=&]+)/g, (...arg) => {
let [, key, value] = arg;
query[key] = value;
});
}

let result = {
protocol,//=>protocol:protocol
hostname,
port,
pathname,
search,
hash,
query
};
console.log(result);
*/

33. call、apply、bind的区别
Call apply的做用
1.改变函数中的THIS(而且让函数执行)
2.能够基于CALL让类数组借用数组原型上的方法(例如:借用SLICE实现把类数组转换为数组)
3.能够基于CALL实现继承
4.能够基于APPLY获取数组中的最大值和最小值

都是用来改变this的指向的
call 传递的是数值
apply 传递的是数组
call、apply对函数直接调用
Bind 执行方法返回一个函数


//3.基于APPLY
// console.log(Math.max.apply(null, ary));

//4.基于ES6的展开运算符
// console.log(Math.max(...ary));

34. 有两个升序数组,而后将他们合为 一个数组并进行升序排序?
let ary1 = [1, 2, 3, 4, 5],
ary2 = [2, 3, 4, 5, 6];
// let ary = ary1.concat(ary2).sort((a, b) => a - b);
let ary = [...ary1, ...ary2].sort((a, b) => a - b);
console.log(ary);

35. 瀑布流的实现原理
1.并排排列三列,三列没有具体的高度,靠内容撑开
2.经过API接口地址,基于AJAX,从服务器端获取数据,拿出数据的前三项依次插入到三列中(数据绑定)
3.计算目前三列的高度,按照高度由小到大把三列进行排序,再次拿出获取数据中的三条,按照排好序的LI依次插入......一直基于这个规律插入完成便可
4.当用户下拉到页面底部,加载更多的数据便可

36. 图片延迟加载怎么实现
/*
* 图片延迟加载(图片懒加载)
* 前端性能优化的重要手段之一,开始加载页面的时候,并无加载真实的图片,当页面结构和数据都呈现完成后,在加载真实的图片
*
* 1.在结构上,咱们把IMG图片放到一个DIV盒子中,开始的时候图片的SRC(SRC中有地址就按照地址加载图片)为空,咱们把图片的地址存放到自定义属性DATA-SRC中(此位置不展现真实的图片),咱们给图片所在的盒子设置一个默认的背景图片占位(要求这张图片越小越好 1KB)
* 2.在JS中,当监听到页面中的结构和数据都加载完成后(或者设置一个间隔时间),开始把DATA-SRC自定义属性中存储的真实图片地址赋值给IMG的SRC属性(浏览器此时开始加载真实的图片 =>为了防止图片地址不存在致使的404错误,咱们在赋值给图片的SRC属性时,每每都会验证一下图片是否存在)
*/

//=>当页面加载完成(结构、数据、DOM等都加载完成)
// window.onload=function(){}
//=>也能够设置一个定时器,间隔多长时间后在加载真实图片(定时器是异步的,因此定时器能执行,也表明页面加载完成)
// setTimeout(function(){},100);

var imgBox = document.getElementById('imgBox'),
pageImg = imgBox.getElementsByTagName('img')[0];
setTimeout(function () {
//=>加载真实图片
var trueImg = pageImg.getAttribute('data-src');

//=>建立一个临时的IMG来验证
// var tempImg = document.createElement('img');
var tempImg = new Image();
tempImg.onload = function () {
//=>图片加载成功触发这个事件
pageImg.src = trueImg;
pageImg.style.display = 'block';
tempImg = null;
};
tempImg.src = trueImg;//=>在部分IE浏览器中只有把SRC赋值放到ONLOAD下面才能起到做用


//=>这样作很差:若是图片不存在,在部分浏览器中,页面中的IMG部分显示的是一个叉叉,很差看(咱们最好在赋值给页面的SRC属性的时候,先验证一下图片是否存在,存在咱们在赋值)
// pageImg.src = trueImg;
// pageImg.style.display = 'block';
}, 1000);


/// 图片延迟加载
$(function () {
let $container = $('.container'),
$imgList = null;

//1.先绑定数据
~function () {
let str = ``;
for (let i = 0; i < 100; i++) {
let ran = Math.round(Math.random() * 3 + 1);
str += `<div class="imgBox">
<img src="" alt="" data-src="img/banner${ran}.jpg">
</div>`;
}
$container.html(str);

$imgList = $container.find('img');
}();

//2.加载真实的图片
//=>lazyImg:单张图片延迟加载(传递给我谁,我就加载谁)
let lazyImg = curImg => {
let $curImg = $(curImg),
trueImg = $curImg.attr('data-src');
let tempImg = new Image();
tempImg.onload = () => {
// $curImg.attr('src', trueImg).css({
// display: 'block'
// });
$curImg.attr('src', trueImg).stop().fadeIn(300);//=>结束当前正在运行的动画,执行FADE-IN,让图片300MS内渐现出来(JQ中提供的动画方法)
tempImg = null;
curImg.isLoad = true;//=>图片加载成功后,设置一个自定义属性存储当前图片已经加载了,后期不须要重复的加载
};
tempImg.src = trueImg;
};

//=>computedImg:计算哪张图片能够加载了
let computedImg = () => {
//=>观察全部图片中谁能加载了,就执行LAZY-IMG让其加载便可
$imgList.each((index, curImg) => {
//=>A:当前图片所在盒子的底边距离BODY偏移
//=>B:当前浏览器底边距离BODY偏移
let $curImg = $(curImg),
$imgBox = $curImg.parent(),
A = $imgBox.offset().top + $imgBox.outerHeight(),
B = document.documentElement.scrollTop + document.documentElement.clientHeight;
if (A <= B) {
//=>表明图片所在盒子呈如今视野中,开始加载真实的图片
if (curImg.isLoad) {
//=>当前图片若是已经加载过了,不在重复的加载
return;
}
lazyImg(curImg);
}
});
};
$(window).on('load scroll', computedImg);//=>LOAD和SCROLL的时候作相同的事情(JQ中的事件绑定特色)
});

37. 写出完整的验证函数
1)长度不能小于6位
2)首字母必须是字母
3)合法字符只能是数字、字母、下划线

let reg = /^[a-zA-Z]\w{5,}$/;

38. 使用jquery实现点击按钮弹出一个对话框(对话框在整个页面正中间,而且最初页面中没有任何的HTML标签)?

$(function () {
//=>当页面结构加载完成执行函数
$('#link').on('click', function () {
$('<div class="center"></div>').appendTo(document.body);
});
});

39. 怎么避免全局变量的污染?
闭包或是单列模式

40.
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();

41.在函数式编程当中有一个很重要的概念就是函数组合,实际上就是把处理数据的函数像管道同样链接起来,而后让数据穿过管道获得最终的结果。例如:
const add1 = (x) => x + 1;
const mul3 = (x) => x * 3;
const div2 = (x) => x / 2;
div2(mul3(add1(add1(0)))) //=>3
而这样的写法可读性明显太差了。咱们能够构建一个 compose 函数,它接受任意多个函数做为参数(这些函数都只接受一个参数),而后 compose 返回的也是一个函数,达到如下的效果:
const operate = compose(div2, mul3, add1, add1)
operate(0) //=>至关于div2(mul3(add1(add1(0))))
operate(2) //=>至关于div2(mul3(add1(add1(2))))
简而言之:compose 能够把相似于 f(g(h(x))) 这种写法简化成 compose(f, g, h)(x)。请你完成 compose 函数的编写。
额外挑战:你能经过 1~2 行代码实现 compose 吗。

=>把数组拼成字符串,把字符串EVAL了便可
const add1 = (x) => x + 1;
const mul3 = (x) => x * 3;
const div2 = (x) => x / 2;
[div2, mul3, add1, add1]
'div2(mul3(add1(add1(0))))'
const compose = (...arg) => {//=>不销毁的栈
//=>arg:[div2, mul3, add1, add1]
return val => {
//=>fn(0):执行的是小函数,val=0
let str = '';
arg.forEach(item => (str += item.name + ','));
str = str.replace(/,/g, '(');
str += val;
arg.forEach(item => (str += ')'));
return eval(str);
}
};
let fn = compose(div2, mul3, add1, add1);
console.log(fn(0));//=>div2(mul3(add1(add1(0))))

// * 柯理化函数编程思想
* 1.执行一个方法,传递一些参数进去,首先造成一个不销毁的栈,把传递的这些值存储起来(没有当即使用,属于预先存储一下)
* 2.返回一个小函数给栈外面
* 3.当执行返回的小函数时候,把以前第一步预先存储的信息拿过来使用(做用域链、闭包等机制完成的)
*
* 咱们把JS中基于闭包实现的预先存储的思想成为 “柯理化函数思想”
*/
const compose = (...arg) => {
//=>arg:[div2, mul3, add1, add1]
arg = arg.reverse();//=>[add1, add1,mul3,div2]
return val => {
//=>val:0
arg.forEach(item => {
val = item(val);
//第一次 add1(0) =>1 =>val=1
//第二次 add1(1) =>2 =>val=2
//第三次 mul3(2) ...
});
return val;
}
};
let fn = compose(div2, mul3, add1, add1);
console.log(fn(0));//=>div2(mul3(add1(add1(0))))


42. 点击每个li能够建立出对应的对象(能够不兼容低版本浏览器)
[结构]
<ul>
<li><a href=' http://xxx'>xxx</a></li>
<li><a href=' http://sss'>sss</a></li>
</ul>
点击第一个LI建立对象:
{
index:1,
name:'xxx',
link:' http://xxx'
}
一样点击第二个LI建立对象
{
index:2,
name:'sss,
link:' http://sss'
}
....

$('#nav>li>a').on('click', function (ev) {
//=>this:当前点击的A
//=>$(this):当前点击的A(JQ对象)
//=>阻止点击A标签页面跳转的行为
ev.preventDefault();
//=>准备数据
let $this = $(this),
$p = $this.parent();
let obj = {
index: $p.index() + 1,
name: $this.text(),
link: $this.attr('href')
};
console.log(obj);
});



43. 分析此函数的做用,补全1/2处的代码


44. 获取数据中的最大值
let ary = [12, 23, 24, 35, 16];
=>获取数组中的最大值
1.数组先排序,而后获取第一个和最后一个就是最大最小值0
console.log(ary.sort((a, b) => b - a)[0]);

2.假设法:假设第一个是最大的,让其和后面每一项进行比较,若是当前项大于假设的值,修改假设的值
let max = ary[0];
ary.slice(1).forEach(item => {
item > max ? max = item : null;
});
console.log(max);

45. 编写一个函数,把一个列表中的每一项反序
<ul id='target'>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
46. 编写一个函数实现数组扁平化
let ary = [1,[2,[3,[4,5]]],6]; //=>[1,2,3,4,5,6]

第一种
let str=JOSN.stringify(ary)
ary=str.replace(/(\[|\])/,"").split(',');

第二种
str=str.replace(/(\[|\])/,"")
str='['+str+']'
ary=JOSON.parse(str);

第三种
let result=[]
let fn=function(ary){
if(ary.length==0) return;
for(let i=0;i<ary.length;i++)
{
let item=ary[i];
if(typeOf item==='Object')
{
fn(item)
}else{
result.push(item)
}
}
}


47. 网页中实现一个计算,计算当年还剩多少时间的倒数计时程序,要求网页上显示 “xxxx年还剩xx天xx时xx分xx秒”;(获取当前时间采用new Data()便可)



48. offsetHeight/clientHeight/scrollHeight的区别
clientHeight:可视区域的高度
offsetHeight当前对象元素的高度
scrollHeight对象元素的真实高度

49. 获取字符串中出现次数最多的字符及出现的次数

let str = 'zhufengpeixunzhouxiaotian';
/*
* 思路一:获取字符串中的每个字母,而后以对象键值对的方式存储起来(属性名是字符,属性值是出现的次数)
*/
//1.获取每个字符出现的次数,以及出现的最大次数
let obj = {},
max = 1,
result = [];
str.replace(/./g, char => {
if (obj.hasOwnProperty(char)) {
obj[char]++;
if (obj[char] > max) {
max = obj[char];
}
return;
}
obj[char] = 1;
});
//2.获取和MAX相匹配次数的字符
for (let char in obj) {
if (obj.hasOwnProperty(char)) {
if (obj[char] === max) {
result.push(char);
}
}
}
console.log(`最多出现的次数是:${max} 次,对应的字符有:${result}`);
//
// /*
// * 思路二:先把字符串中的每个字符变为数组中的每一项,给数组排序,在变为字符串(相同的字符挨着),在基于正则捕获替换
// */
let max = 1;
str = str.split('').sort().join('');//=>'aaeefghhiiinnnooptuuuxxzz'
str = str.replace(/(.)\1*/g, (...arg) => {
let [value, char] = arg,
len = value.length;
len > max ? max = len : null;
return `${char}{${len}}`;
});
// console.log(str);//=>'a{2}e{2}f{1}g{1}h{2}i{3}n{3}o{2}p{1}t{1}u{3}x{2}z{2}'
// let reg =/([^\d{}])\{"+max+"\}/g;//=>字面建立正则的方式,正则中的每个字符都是元字符,不能实现把一个变量的值做为正则一部分的需求
let reg = new RegExp('([^\\d{}])\\{' + max + '\\}', 'g');
str.replace(reg, (...arg) => {
console.log(arg[1]);
});

50. 完成如图所示的文字横向无缝衔接滚动的“跑马灯”效果
相关文章
相关标签/搜索