CSS 面试题目
1、padding和margin有什么不同
- 作用的对象不同,margin作用对象是外部,padding作用于自身
- 视距上,margin改变位置,padding改变大小
2、vw,vh和百分比的区别
- vw是根据视窗大小决定,百分比根据父级决定
3、行内元素和块级元素区别
- 行内元素大小由内容决定,不能改变大小,块级元素会占满整行(可以改变大小)
4、如何让谷歌支持小字体
- 谷歌默认支持最小12px,可以通过transform:scale()变小
JavaScript 面试题目
1、let和var
- var变有声明提升的问题
- var没有局部作用域
function fn(){
for(var i=0;i<5;i++){
}
console.log(i) // i === 5
}
- var会有声明覆盖的问题
var name ="aaa"
var name = "bbb"
console.log(name) // bbb
2、深拷贝和浅拷贝
// 引用类型才有有深拷贝浅拷贝的问题
// 1、数组和对象的赋值都叫做 浅拷贝
let arr = [1,2,3]
let newArr = arr;
newArr.push(4)
console.log(arr,newArr) // [1,2,3,4],[1,2,3,4] 藕断丝连
// 2、结构赋值不会影响到原数组,对象
let arr2 = [1,2,3]
let newArr2 =[...arr2]
newArr2.push(4)
console.log(arr2,newArr2) // [1,2,3],[1,2,3,4]
// 3、解构赋值也会影响到原数组,说明它也是浅拷贝
// 针对一维数组对象,不会影响原数组对象,但是多维就不行了
let arr3 = [[1,2,3],[4,5,6]]
let newArr3 =[...arr3]
newArr3[0].push(999)
console.log(arr3,newArr3) // [[1,2,3,999],[4,5,6]] [[1,2,3],[4,5,6]]
// 深拷贝用法
let list=[
{id:1,stuName:"小明",age:11},
{id:2,stuName:"小红",age:12},
{id:3,stuName:"小黄",age:13}
]
let newList = JSON.parse(JSON.strignify(list)) // json 深拷贝 ;没办法转换function
newList.push({id:888})
console.log(list,newList) // 不会影响原数组,只有newList加了id:888
function deepClose(source){
// [] => Array 基类 {} => Object
const targetObj = source.constructor === Array ? [] : {}
for(let keys in source){
if(source.hasOwnProperty(keys)){
// keys => 1.基础数据类型,2.对象,3.数组
if(source[keys] && typeof source[keys] === 'object' ){
targetObj[keys] = deepClose(source[keys])
}else {
// 基本数据类型,直接赋值
targetObj[keys] = source[keys]
}
}
}
return targetObj
}
性能优化
1、浏览器输入URL做了什么
- 输入 www.baidu.com // 统一资源定位符,俗称网址
- 1、第一次访问
- 2、解析URL,并且去DNS域名系统匹配
- 3、拿到真实的IP ,建立连接(TCP3次握手)
- 4、拿到数据,渲染页面
- 5、拿到数据后,断开连接(四次挥手)
html->dom树
HTML和css并行构建 -> render tree -> 计算布局信息 -> UI引擎渲染 -> 用户见到的页面
css->css结构体 回流 重绘
- 6、第二次访问
- 7、浏览器会将解析的IP地址存放在本地 => 读取浏览器缓存的IP
2、从哪些点做性能优化
- 1、加载上的优化
- 减少http请求(精灵图,文件的合并)
- 减小文件大小(资源压缩,图片压缩,代码压缩)
- CND(第三方库引用CDN)
- 预加载(SSR服务端渲染)
- 懒加载
- 分包
- 2、性能方面
- 减少dom操作,避免回流,文档碎片(使用transform:translate -> 脱离正常文档流,避免回流)
3、真正的性能优化
- 页面加载性能(加载时间,用户体验)
- 动画与操作性能 是否流畅
- 内存占用 内存占用过大,浏览器崩溃
- 电量消耗
4、懒加载
<body>
<img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
<img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
<img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
<img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
<img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
<img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
<img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
<img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
<img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
<img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
</body>
<script>
let num = document.getElementsByTagName("img").length; // 9
let img = document.getElementsByTagName("img")
let n = 0
lazyLoad()
function lazyLoad (){
let windowsHeight = document.documentElement.clientHeight //可见区域
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop // 滚动距离
for(let i=n;i<num;i++){
if(img[i].offsetTop < windowsHeight+scrollTop){
if(img[i].getAttribute("src") === "./img/loading.gif"){
img[i].src = img[i].getAttribute("data-src") // 备胎转正
}
}
}
}
</script>
5、this的指向问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
console.log(this) // window
function a(){
console.log(this) // window
}
a()
const obj ={
a:1,
b:{
a:1,
fn:function(){
console.log(this) // b ;this指向上一个调用者
}
}
}
obj.b.fn()
// call、apply、bind可以改变this指向
// call 和 apply 会立即执行 apply 不会
</script>
</body>
</html>
6、闭包
- 避免变量被污染
- 私有化
- 保存变量,常驻内存
- 闭包:方法里放一个方法
// 闭包应用 => 处理私有数据
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 c = makeCounter
c.increment()
c.decrement()
c.value()
7、new
function Person(){
this.name = "xiaohong"
this.fn = function(){
console.log(`名字是:${this.name}`)
}
}
let person1 = new Person(); // new 的时候Person才是构造函数
person1.fn()
// new的过程
// 1、创建一个空对象
let obj = new Person()
// 2、设置原型链 __proto__ 指向new 出来的类
obj._proto_ = Person.prototype
// 3、改变this的指向
let result = Person.call(obj)
// 4、判断返回值类型
// 构造函数默认返回新创建的对象
// 普通函数默认返回undefined
if(typeof result === "object"){
person1 = result
}else {
person1 = obj
}
8、vue底层原理
-
如何实现VUE2.x的响应式
-
发布订阅模式+双向数据绑定 = 基本的响应式
<!-- 1、 订阅器模型 --> let Dep = { clientList:{}, <!-- 添加订阅 --> lister:function(key,fn){ (this.clientList[key] || (this.clientList[key] = [])).push(fn) }, <!-- 推送方法 --> trigger:function(){ let key = Array.prototype.shift.call(arguments), fns = this.clientList[key]; if(!fns || fns.length === 0) return false for(let i = 0,fn;fn = fns[i++]){ fn.apply(this,arguments) } } } <!-- 数据劫持 --> let dataHijack = function({data,tag,datakey,selector}){ let value = "",el = document.querySelector(selector) Object.defineProperty(data,datakey,{ get:function(){ console.log("取值") return value }, set:function(val){ value = val; Dep.trigger(tag,val) } }) <!-- 添加订阅者 --> Dep.listen(tag,function(text){ el.innerHTML = text }) }
9、Vue3 的响应式原理
let obj = {
name:"小明",
age:18
}
const p = new Proxy(obj,{
// target:原数据
get(target,propName){
console.log(`读取P的${propName}属性`)
return target[propName]
},
set(target,propName,value){
console.log(`修改P的${propName}属性`)
target[propName] = value
},
deleteProperty(target,propName){
console.log(`删除P的${propName}属性`)
return delete target[propName]
}
})
9、手写Promise
function myPromise (excutor){
// 1、执行结构
let self = this
self.status = 'pending'; // 状态
self.value = null; // 成功结果
self.reason = null; // 失败原因
// 6、添加缓存数组
self.onFulfilledCallbacks = []
self.onRejectedCallbacks = []
// 4、判断状态,做出相应的处理
function resolve(value){
if(self.status === "pending"){
self.value = value // 保存成功结果
self.status = "fulfilled"
// 7、状态改变,依次取出
self.onFulfilledCallbacks.forEach(item=>item(value))
}
}
function reject(reason){
if(self.status === "pending"){
self.reason = reason // 保存失败原因
self.status = "reject"
// 7、状态改变,依次取出
self.onRejectedCallbacks.forEach(item=>item(value))
}
}
// 3、new的时候执行一次
try{
excutor(resolve,reject)
}catch{
reject(err)
}
}
// 2、then方法
myPromise.prototype.then = function (onFulfilled,onRejected){
// 5、 状态改变后 调用.then方法
onFulfilled = typeof onFulfilled === "function"? onFulfilled : function(data){resolve(data)}
onRejected = typeof onRejected === "function"? onRejected :function(err){throw(err)}
let self = this
if(self.status === "pending"){
self.onFulfilledCallbacks.push(onFulfilled)
self.onRejectedCallbacks.push(onRejected)
}
}
let demo = new myPromise((resolve,reject)=>{
})