面试题小结

css部分


css中的外边距叠加问题,怎么造成对的,怎么解决:

会发生margin重叠的肯定是同一个BFC内的块级元素,例如div、ul等,不是块级元素不会发生重叠。(内联元素是不能设置高、行高、内外边距的,而且内联元素只能容纳文本或者其他内联元素。)

重叠的情况大致可以分为以下几种:

1、当一个元素出现在另一个元素上面时,第一个元素的下外边距与第二个元素的上外边距会发生合并。

2、当一个元素包含在另一个元素中时(假设没有内边距或边框把外边距分隔开),第一个子元素的上边距会和父元素的上边距合并;最后一个子元素的下边距会和父元素的下边距合并。

3、假设有一个空元素,它有外边距,但是没有边框或填充。在这种情况下,上外边距与下外边距就碰到了一起,它们会发生合并。如果这个外边距遇到另一个元素的外边距,它还会发生合并。

当发生重叠的时候:

1、当两个margin都是正值的时候,取两者的最大值;

2、当 margin 都是负值的时候,取的是其中绝对值较大的,然后,从 0 位置,负向位移;

3、如果两个margin值为一正一负,比如一个是margin-top,一个是margin-bottom取两个值的最大值

只有普通文档流中块框的垂直边界才会发生边界叠加。行内框、浮动框或绝对定位框之间的边界不会叠加。

 

1.处于静态流元素会发生合并,所以float和position:absolute都不会发生合并
2.设置为inline-block ,也不会发生合并

针对父元素和子元素情况不合并方法:
1.设置了清除浮动属性
2.因为margin需要直接接触才能合并,所以父元素或子元素中有border或padding,或者二者之间有元素

注意:
1.如果两个外边距值中有一个为0,也会发生合并。
2.如果有负外边距,合并后外边距为最大正边距加上最小负边距(绝对值最大的一个),如上面元素下边距为20px,下面元素上边距为-20px,则最后为0px

js部门


1.合并数组中相邻且重复的元素
说明:请实现一个函数 merge,传入一个数组,合并数组中【相邻且重复】的元素。
示例:
merge([3,2,2,4,5,5,6,2,1]); // 输出[3,2,4,5,6,2,1]
merge([3,2,3]); // 输出[3,2,3]
merge([2,2,3]); // 输出[2,3]
function merge(arr){
let temp = [];
for (var i=0;i<arr.length;i++){
if(temp.indexOf(arr[i]) ==-1){
  temp.push(arr[i])
}
  return temp
}
}
merge([3,2,2,4,5,5,6,2,1]);
 
temp.indexOf(arr[i])打印出来是,说明不是重复打印不是-1
 
const arr1 = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
const arr2 = [];
for (let i = 0; i < arr1.length; i++) {
  if (arr1.indexOf( arr1[i] ) === i) {
    arr2.push( arr1[i] );
  }
}

console.log( arr2 );
// [1, 2, 3, 5, 4]
console.log( arr1 );
// [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]

  

const arr1 = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
const arr2 = arr1.filter( (element, index, self) => {
    return self.indexOf( element ) === index;
});

console.log( arr2 );
// [1, 2, 3, 5, 4]
console.log( arr1 );
// [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]

  

 2.用快速排序的思想实现一个快速排序

(1).建立一个中间的数

(2).中间的数和左边和右边的数进行比较

(3).将比较完的数结合在一起

function quickSort(arr) {
    if(!arr instanceof Array){
      return;
    }
    if(arr.length <=1){
      return arr;
    }
    // 获取中间的数值的索引
    var midelmath = Math.floor(arr.length/2);
    // 获取中间的数值
    var value = arr.splice(midelmath,1);
    console.log(value,midelmath,'value');
    
    var left = [];
    var right = [];
    for(var i=0;i<arr.length;i++){
      if(arr[i]<value){
        left.push(arr[i])
      }else{
        right.push(arr[i])
      }
    }
    return quickSort(left).concat(value,quickSort(right));
  }
  console.log(quickSort([10,34,21,56,78,23,1,66]));

3.什么是防抖和节流?有什么区别?如何实现?

触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间

<div id="inp">所以,render方法会根据tagName构建一个真正的DOM节点,然后设置这个节点的属性,最后递归的把自己的子节点也构建起来,所以只需要调用ul的render方法,通过document.body.appendChild就可以挂载到真实的页面了。所以,render方法会根据tagName构建一个真正的DOM节点,然后设置这个节点的属性,最后递归的把自己的子节点也构建起来,所以只需要调用ul的render方法,通过document.body.appendChild就可以挂载到真实的页面了。所以,render方法会根据tagName构建一个真正的DOM节点,然后设置这个节点的属性,最后递归的把自己的子节点也构建起来,所以只需要调用ul的render方法,通过document.body.appendChild就可以挂载到真实的页面了。所以,render方法会根据tagName构建一个真正的DOM节点,然后设置这个节点的属性,最后递归的把自己的子节点也构建起来,所以只需要调用ul的render方法,通过document.body.appendChild就可以挂载到真实的页面了。所以,render方法会根据tagName构建一个真正的DOM节点,然后设置这个节点的属性,最后递归的把自己的子节点也构建起来,所以只需要调用ul的render方法,通过document.body.appendChild就可以挂载到真实的页面了。</div>
 #inp{
    height: 300px;
     200px;
    border: 1px solid #cccccc;
    overflow-y: auto;
    overflow-x: visible;
  }
function debounce(fn) {
      let timeout = null; // 创建一个标记用来存放定时器的返回值
      return function() {
        clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
        timeout = setTimeout(() => {
          // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
          fn.apply(this, arguments);
        }, 500);
      };
    }
    function sayHi() {
      console.log("防抖成功");
    }

    var inp = document.getElementById("inp");
    inp.onscroll= debounce(debounce(sayHi))

  高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率

function throttle(fn) {
        let canRun = true; // 通过闭包保存一个标记
        return function () {
          if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
          canRun = false; // 立即设置为false
          setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
            fn.apply(this, arguments);
            // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
            canRun = true;
          }, 500);
        };
      }
      function sayHi(e) {
        console.log(e.target.innerWidth, e.target.innerHeight);
      }
      window.addEventListener('resize', throttle(sayHi));

  

setTimeout、Promise、Async/Await 的区别

三个虽然都是异步的执行函数但是有好多不同

settimeout是延后执行的函数

console.log('script start')	//1. 打印 script start
setTimeout(function(){
    console.log('settimeout')	// 4. 打印 settimeout
})	// 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
console.log('script end')	//3. 打印 script start
// 输出顺序:script start->script end->settimeout

Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。
console.log('script start')
let promise1 = new Promise(function (resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {
    console.log('promise2')
})
setTimeout(function(){
    console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout

  async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。

async function async1(){
   console.log('async1 start');
    await async2();
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}

console.log('script start');
async1();
console.log('script end')

// 输出顺序:script start->async1 start->async2->script end->async1 end

  map,foreach有什么区别?

  • 都是循环遍历数组中的每一项
  • forEach和map方法里每次执行匿名函数都支持3个参数,参数分别是item(当前每一项),index(索引值),arr(原数组)
  • 匿名函数中的this都是指向window
  • 只能遍历数组
  • 都不会改变原数组

区别:

1.map方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。
2.map方法不会对空数组进行检测,map方法不会改变原始数组。

无论arr是不是空数组,forEach返回的都是undefined

foreach跳出循环用try catch包起来

 map方法遍历:

 创建对象自变量的几种方式:

1. 对象字面量方式。

var per = {
name:'zhangsan',
age:25,
job:'html',
sayName:function(){
alert(this.name);
}
}

2、工厂模式

工厂模式抽象了创建具体对象的过程。由于在ECMAScript中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节,如下面的例子:

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('zhang',30,'java');
var person2 = createPerson('zhao',25,'php');

函数createPerson()能够根据接受到的参数来构建一个包含所有必要信息的Person对象。可以无数次的调用这个函数,而每次它都会返回一个包含三个属性和一个方法的对象。
缺点:工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

3、构造函数模式

function Person(name,age,job){
this.name= name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}

var person1 = new Person('zhang',30,'java');
var person2 = new Person('zhao',25,'php');

创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型。而这正是构造函数模式胜过工厂模式的地方。
然而,使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。在上面的例子中,person1和person2都有一个名为sayName()的方法,但那两个方法不是同一个Function的实例,创建两个完成同样任务的Function实例的确没有必要;况且有this对象在,根本不用在执行代码前就把函数绑定到特定对象上面。因此可以像下面这样,通过把函数定义转移到构造函数外部来解决这个问题。

4、原型模式

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有的对象实例共享他所包含的属性和方法。

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有的对象实例共享他所包含的属性和方法。

function Person(){}
Person.prototype.name = 'zhang';
Person.prototype.age = '22';
Person.prototype.job = 'html5';
Person.prototype.sayName = function(){
alert(this.name);
};

var person1 = new Person();
var person2 = new Person();

原型模式也不是没有缺点。首先,它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。虽然这会在某种程度上带来一些不方便,但还不是原型的最大问题。原型模式的最大问题是由共享的本性所导致的。
原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性倒也说的过去,通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然后,对于包含引用类型的属性来说,问题就比较突出了。
---------------------
作者:zhang070514
来源:CSDN
原文:https://blog.csdn.net/zhang070514/article/details/77609387
版权声明:本文为博主原创文章,转载请附上博文链接!

js的继承方式:https://www.jianshu.com/p/b76ddb68df0e

还有通过es6中的extend的方法,props(),来实现继承。

class Animal{
	constructor(){
	this.type = 'animal'
	}
	says(say){
	console.log(this.type + " say "+ say)
	}
	}
	let animal = new Animal();
	animal.says('hello')
	class Dog extends Animal{
		constructor() {
		super();
		this.type = 'dog'
	}
		
	}
	let dog = new Dog();
	dog.says('hellop')

  

网络请求部分


一、ajax的五种状态(readyState 

  0 - (未初始化)还没有调用send()方法
  1 - (载入)已调用send()方法,正在发送请求
  2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
  3 - (交互)正在解析响应内容
  4 - (完成)响应内容解析完成,可以在客户端调用了

(0)未初始化

此阶段确认XMLHttpRequest对象是否创建,并未调用open()方法进行未初始化作好准备。值未0表示对象已经存在,否则浏览器会报错---对象不存在。

(1)载入
此阶段对xml(标准化越来越近了)HttpRequest对象进行初始化,即调用open()方法,根据参数(method,url,true)完成对象状态的设置。并调用send()方法开始向服务端发送请求。值为1表示正在向服务端发送请求。

(2)载入完成
此阶段接收服务器端的响应数据。但获得的还只是服务端响应的原始数据,并不能直接在客户端使用。值为2表示已经接收完全部响应数据。并为下一阶段对数据解析作好准备。

(3)交互
此阶段解析接收到的服务器端响应数据。即根据服务器端响应头部返回的MIME类型把数据转换成能通过responseBody、responseText或responsexml(标准化越来越近了)属性存取的格式,为在客户端调用作好准备。状态3表示正在解析数据。

(4)完成
此阶段确认全部数据都已经解析为客户端可用的格式,解析已经完成。值为4表示数据解析完毕,可以通过xml(标准化越来越近了)HttpRequest对象的相应属性取得数据。

---恢复内容结束---

原文地址:https://www.cnblogs.com/smdb/p/10579760.html