1. div快速居中方法?
父级元素display:flex;
子级元素margin:auto;
2. padding和margin的区别?
作用对象不同
padding:改变盒子本身大小(作用于自身)
margin: 不改变盒子大小(作用于外部)
3. vw和百分比有什么区别?
vw不继承和设备的宽度有关系
百分比有继承和父级宽度有关
4. 如何让谷歌浏览器支持小字体?
缩放
transform:scale(0.8);
-webkit-transform:scale(0.8); //是针对webkit内核的浏览器(如Chrome和Safari)的私有属性前缀,以确保在这些浏览器中也能正确显示缩放效果
5. let和var的区别?
var 有变量提升 =>先上车后买票
console.log(name) //输出为:小明同学 并非:undefined
var name = "小明同学"
var 没有局部作用域 =>红杏出墙
function fn2() {
for(var i=0;i<5;i++) {
}
console.log(i) //5
var 存在声明覆盖 =>套牌车
var name = "大郎"
var name = "西门官人"
console.log(name) //西门官人
let反之 使用let更规范
6. 深拷贝和浅拷贝
### //1.基本数据类型
//这属于赋值和拷贝没有关系 在内存中建立了自己单独的储存空间
//必须区分的话属于深拷贝
let a = 5;
let b = a;
b= 3;
console.log(a,b) // 5,3
### //2.数组与对象的赋值叫做浅拷贝=>藕断丝连
let arr= [1,2,3];
let newArr=arr;
newArr.push(4)
console.log(arr,newArr) //1,2,3,4 1,2,3,4
### //3.解构赋值是深拷贝还是浅拷贝
#### //3.1解构赋值针对*一维数组*可以认为是深拷贝
let arr=[1,2,3];
let newArr = [...arr];
newArr.push(4);
consloe.log(arr,newArr) //1,2,3 1,2,3,4
#### //3.2解构赋值针对*多维数组*属于浅拷贝
let arr2 = [[1,2,3],[4,5,6]];
let newArr2 = [...arr2];
newArr2[0].push(666);
console.log(arr2,arrArr2) //[1,2,3,666],[4,5,6] [1,2,3,666],[4,5,6]
#### //3.3深拷贝用法
let list =[
{id:1,stuName:'小明',class:'无年级二班'},
{id:1,stuName:'小红',class:'无年级三班'},
{id:1,stuName:'小绿',class:'无年级四班'},
]
let newList = JSON.parse(JSON.stringify(list)); //常见的深拷贝方式80%的场景 因为如果有方法会拷贝不出来 把function转化为'function'
newList.push({id:888})
console.log(list,newList) //list不会被加上id:888
#### //3.4标准的深拷贝 => 专门针对引用数据类型(数组,对象)
function deepClone (source) {
//[] =>Array(基类) { } => object
const targetObj = source.constructor === Array ? [] : {};
for (let keys in source) {
if (source.hasOwnProperty(keys)) {
//keys=> 3 有三中情况
//引用数据类型
if (source[keys] && typeof source[keys] === 'object') {
// 下面这行 运行时可以去掉 只是说明一下在内部判断时可能出现多种情况
targetObj[keys] = source[keys].constructor === Array ? [] : {}; //容错代码 提示代码(维护代码)
//递归
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys];
}
}
}
return targetObj
}
let objC = {
ff: 'name',
gg: 1,
obj: { str: '1111', age: 12 },
arr: [1, 2, 3, 4]
}
let newObjc = deepClone(objC);
newObjc.ff = "有钱不买收音机";
newObjc.arr.push("就听我吹牛逼");
console.log(objC, newObjc)
7.输入URL的一瞬间浏览器都做了什么?
8.三次握手
第一次握手:客户端向服务端发起建立连接请求
握手前:客户端的状态为close,服务端的状态肯定也是close
握手后:客户端状态为SYN-SENT(同步发送),服务端状态为listen(倾听)
第二次握手:服务端收到客户端的报文会给客户端返回一段报文
握手前:客户端状态为SYN-SENT(同步发送),服务端状态为 listen(倾听)
握手后:客户端状态为SYN-SENT(同步发送),服务端状态为SYN-RCVD
第三次握手:客户端收到服务端的报文,再向服务端发送报文
握手前:客户端状态为SYN-SENT(同步发送),服务端状态为SYN-RCVD
握手后:客户端和服务端状态都为estadlished(建立连接)
握手的目的:客户端与服务端互相确定彼此是否有接收,发送的能力。
9.闭包
1.什么是闭包
闭包是指有权访问另一个函数作用域中的变量的函数。
//示例
// 闭包的应用 处理私有化数据
let makeCounter = function () {
let privateCounter = 0;
function changeBy (val) {
privateCounter += val;
}
return {
increment: function () {
changeBy(1);
},
decrement: function () {
changeBy(-1);
},
value: function () {
return privateCounter;
}
}
};
let fun1 = makeCounter();
fun1.increment()
console.log(fun1.value());
-----------------------------------------------------------------------------------------------------------
//思考题 点击li打印li对应的下标
<body>
<ul>
<li class="box">i</li>
<li class="box">i</li>
<li class="box">i</li>
<li class="box">i</li>
</ul>
</body>
<script>
// var elements = document.getElementsByTagName('li');
// for (var i = 0; i < elements.length; i++) {
// elements[i].onclick = function () {
// console.log(i); //4
// alert(i); //4
// };
// }
//function()的作用域中没有变量i,所以在父级作用域寻找,父级中有i,且for循环执行完毕,i已被赋值为4
var elements = document.getElementsByTagName('li');
for (let i = 0; i < elements.length; i++) {
elements[i].onclick = function () {
return (function (n) {
console.log(n);
alert(n);
})(i);
};
}
</script>
2.为什么使用闭包
避免变量被污染
私有化
保存变量,常驻内存
3.闭包的使用场景
防抖,节流
10.事件委托(事件代理)
把原本需要自己干的事情委托给父亲或者爷爷或者祖先
原生的js事件委托(点击标签打印标签的内容)
<body>
<ul id="ul">
<li>0</li>
<li>1</li>
<li>2</li>
<span>span0.5span</span>
<li>3</li>
<li>4</li>
</ul>
</body>
<script>
let ul = document.getElementById("ul")
ul.onclick = function (event) {
console.log(event);
event = event || window.event
let target = event.target;
if (target.nodeName == 'LI') {
alert("点击了内容为---------" + target.innerHTML + '----------li')
}
}
</script>
11.apply()–cell()–bind()
三者的区别:
1.apply和call不需要调用 bind需要调用
2.apply的参数都要放在数组中,call和bind对参数类型没有要求,用逗号分隔就行
用法:
let obj = {
name: '小明',
}
let testObj = {
name: '张三',
age: 18,
sex: '男',
saiHi: function (...argument) {
console.log('我是' + this.name, argument);
}
}
// apply()
testObj.saiHi(); // 我是张三 []
// 参数1(必填) 要修改的this指向的对象
testObj.saiHi.apply(obj); // 我是小明 []
// 参数二(可选)数组或伪数组,将其作为参数给调用的函数
testObj.saiHi.apply(obj, [1, 2, 3]); // 我是小明 [1, 2, 3]
testObj.saiHi.apply(obj, { getName: '王五' }); // 我是小明 []
testObj.saiHi.apply(obj, [getName = '王五']); // 我是小明 ['王五']
testObj.saiHi.apply(obj, { getName: '王五', length: 2 }); // 我是小明 [undefined, undefined]
// //call()
// // 参数1(必填) 要修改的this指向的对象
// testObj.saiHi.call(obj); // 我是小明 []
// // 参数二(可选)可以传任意值
// testObj.saiHi.call(obj, [1, 2, 3]); // 我是小明 [1, 2, 3]
// testObj.saiHi.call(obj, { getName: '王五' }); // 我是小明 {getName: '王五'}
// testObj.saiHi.call(obj, { getName: '王五', length: 2 }); // 我是小明 {getName: '王五', length: 2}
// // 参数N(可选)可以传多个值,同参数二
// testObj.saiHi.call(obj, { getName: '王五', length: 2 }, true, 'hello'); // 我是小明 [{getName: '王五', length: 2}, true, 'hello']
// //bind()
// // 参数1(必填) 要修改的this指向的对象
// testObj.saiHi.bind(obj)(); // 我是小明 []
// // 参数二(可选)可以传任意值
// testObj.saiHi.bind(obj, [1, 2, 3])(); // 我是小明 [1, 2, 3]
// testObj.saiHi.bind(obj, { getName: '王五' })(); // 我是小明 {getName: '王五'}
// testObj.saiHi.bind(obj, { getName: '王五', length: 2 })(); // 我是小明 {getName: '王五', length: 2}
// // 参数N(可选)可以传多个值,同参数二
// testObj.saiHi.bind(obj, { getName: '王五', length: 2 }, true, 'hello')(); // 我是小明 [{getName: '王五', length: 2}, true, 'hello']
12.js是如何实现继承的?
1.原型链继承
让一个构造函数的原型是另一个类型的实例,那么这个构造函数new出来的实例就是具有该实例的属性。
// 1.原型链继承
// 定义一个方法parent
function Parent () {
this.isShow = true
this.info = {
name: "abc",
age: 18,
};
}
//在parent方法的原型上添加getInfo方法
Parent.prototype.getInfo = function () {
console.log(this.info);
console.log(this.isShow);
}
// 定义一个child方法
function Child () { };
// **让Child的原型是Parent的实例,new出来的Child的实例就具有Parent实例的属性**
Child.prototype = new Parent();
//**new出来的Child的实例就具有Parent实例的属性**
let Child1 = new Child();
Child1.info.gender = "男";
Child1.getInfo() //{name:'abc',age:18,gender:'男'} true
let Child2 = new Child();
Child2.isShow = false
Child2.info.gender = "女";
Child2.getInfo() //{name:'abc',age:18,gender:'女'} false
优点:写法方便简洁,容易理解
缺点:对象实例共享所有继承的属性和方法。无法向父类构造函数传参。
2.借用构造函数继承
在子类型构造函数的内部调用父类型构造函数:使用apply()或者call()方法将父对象的构造函数绑定在子对象上。
// 定义一个方法parent
function Parent (gender) {
this.info = {
name: "yyy",
age: 18,
gender: gender
};
}
function Child (gender) {
Parent.call(this, gender)
}
let child1 = new Child('男');
child1.info.nickname = 'xxxx'
console.log(child1.info); //{name:'yyy',age:18,gender:'男',nickname:'xxxx'}
let child2 = new Child('女');
console.log(child2.info); //{name:'yyy',age:18,gender:'女'}
优点:解决了原型链实现继承的不能传参的问题和父类的原型共享的问题。
缺点:借用构造函数的缺点是方法都在构造函数中定义,因此无法复用。 在父类的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。
3.组合继承
将原型链 和 借用构造函数 组合在一起。
// 定义一个方法parent
function Parent (gender) {
console.log('121');
this.info = {
name: "yyy",
age: 18,
gender: gender
};
}
Parent.prototype.getInfo = function () {
console.log(this.info.name, this.info.age); //使原型链继承原型上的属性和方法
}
function Child (gender) {
Parent.call(this, gender) //使用构造方法传递参数
}
let child1 = new Child('男');
child1.info.nickname = 'xxxx'
console.log(child1.info); //{name:'yyy',age:18,gender:'男',nickname:'xxxx'}
let child2 = new Child('女');
console.log(child2.info); //{name:'yyy',age:18,gender:'女'}
优点:解决了原型链继承和构造函数继承的缺点。
缺点:每次都会调用两次父类构造函数(创建子类原型和子类构造函数内部)
4.ES6,class实现继承
class通过extends关键字实现继承,其实是先创建出父类的this对象,然后用子类的构造函数修改this
子类的构造方法中必须调用super方法,且只有在调用了super()之后才能使用this,因为子类的this对象是继承父类的this对象,然后对其进行加工,而super方法表示的是父类的构造函数,用来新建父类的this对象。
// 继承关键字 =>extends
// 定义一个 人类
class Person {
// 类似于之前的构造函数
constructor(name, age) {
console.log("触发Person的构造函数");
this.name = name
this.age = age
}
// 底层这两个方法,就是添加到Person.prototype上
sayHi () {
console.log("你好@吖");
}
jump () {
console.log("会跳");
}
}
const p = new Person('张三', 18)
console.log(p);
// 定义一个 老师类 继承于 人类
class Teacher extends Person {
// 如果没有定义构造函数,在继承的时候会默认自动借调父构造函数
constructor(name, age, lesson) {
// 现在写的构造函数中,没有借调父构造函数
super(name, age) //触发调用父构造函数,进行实例的属性初始化
this.lesson = lesson
}
teach () {
console.log("会教书");
}
}
const teacher = new Teacher('zs', 18, '教体育')
teacher.jump()
teacher.sayHi()
teacher.teach()
console.log(teacher);
优点:语法简单易懂,操作方便。
缺点:不是所有的浏览器都支持class关键字