IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 前端面试题系列【1】—类型检测、this、跨域及其解决方案、BFC(万字详解——建议收藏) -> 正文阅读

[JavaScript知识库]前端面试题系列【1】—类型检测、this、跨域及其解决方案、BFC(万字详解——建议收藏)

一、JavaScript类型检测的四种方法

1. typeof

ypeof 是一元运算符,同样返回一个字符串类型。一般用来判断一个变量是否为空或者是什么类型。
除了 null 类型以及 Object 类型不能准确判断外,其他数据类型都可能返回正确的类型。

typeof undefined // 'undefined'
typeof '10'    // 'String'
typeof 10     // 'Number'
typeof false   // 'Boolean'
typeof Symbol()  // 'Symbol'
typeof Function  // ‘function'
typeof null // ‘Object’
typeof []     // 'Object'
typeof {}     // 'Object'

面试官:为什么 typeof null 等于 Object?

  • 不同的对象在底层原理的存储是用二进制表示的,在 javaScript 中,如果二进制的前三位都为 0 的
    话,系统会判定为是 Object 类型。
    null 的存储二进制是 000 ,也是前三位,所以系统判定 null
    为 Object 类型。

  • 扩展:
    000 :对象类型。
    1 :整型,数据是31位带符号整数。
    010 :双精度类型,数据是双精度数字。
    100 :字符串,数据是字符串。
    110 :布尔类型,数据是布尔值。

2. instanceof

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。我
们可以使用 instanceof 来进行判断某个对象是不是另一个对象的实例。(判断谁是谁的实例)
instanceof 的原理是通过判断该对象的原型链中是否可以找到该构造类型的 prototype 类型。

function Foo(){}
var f1 = new Foo();
console.log(f1 instanceof Foo);// true

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FGQY2nPL-1630850642429)(C:\Users\AUSU\AppData\Roaming\Typora\typora-user-images\image-20210905215636711.png)]

3. constructor

对于引用类型,除此之外,还可以使用 xx.constructor.name 构造函数来判断。

console.log(fn.constructor.name)  // Function
console.log(date.constructor.name)// Date
console.log(arr.constructor.name) // Array
console.log(reg.constructor.name) // RegExp
4. Object.prototype.toSrtring.call()

一种最好的基本类型检测方式 Object.prototype.toString.call() ;它可以区分 null 、 string 、
boolean 、 number 、 undefined 、 array 、 function 、 object 、 date 、 math、RegExp 数据类型。

// 判断基本类型
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(“abc”);// "[object String]"
Object.prototype.toString.call(123);// "[object Number]"
Object.prototype.toString.call(true);// "[object Boolean]"
// 判断引用类型
function fn(){
 console.log(“xiaolu”);
}
var date = new Date();
var arr = [1,2,3];
var reg = /[hbc]at/gi;
Object.prototype.toString.call(fn); // "[object Function]"
Object.prototype.toString.call(date); // "[object Date]"
Object.prototype.toString.call(arr); // "[object Array]"
Object.prototype.toString.call(reg); // "[object RegExp]"
//缺点:不能判断谁是谁的实例,可以结合instanceof

二、this

对象调用时,谁调用,this就指向谁
通过new的方式,this永远指向新创建的对象
箭头函数没有自己的this,而是定义时在哪,所在的上下文就是它的this(找箭头函数内部的this,一层层向外剥)

可以结合这个来理解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iwKVz742-1630850642435)(C:\Users\AUSU\AppData\Roaming\Typora\typora-user-images\image-20210905220304036.png)]

三、改变this指向的三种方法( call 、apply、bind )

  • 共同点
    功能角度:三者都能改变 this 指向,且第一个传递的参数都是 this 指向的对象。
    传参角度:三者都采用的后续传参的形式。
  • 不同点
    传参方面: call 的传参是单个传递的(试了下数组,也是可以的),而 apply 后续传递的参
    数是数组形式(传单个值会报错),而 bind 没有规定,传递值和数组都可以。
    执行方面: call 和 apply 函数的执行是直接执行的,而 bind 函数会返回一个函数,然后我
    们想要调用的时候才会执行。

面试官:如果我们使用上边的方法改变箭头函数的 this 指针,会发生什么情况呢?能否进行
改变呢 ?

——大家想一下哈,可以自己试一下,或者私信评论我

1. call
Function.prototype.myCall = function(context) {
            if (typeof this !== 'function') {
                throw new Error('error');
            }
            context = context || window;
            context.fn = this;
            let args = Array.from(arguments).slice(0);
            let result = context.fn(...args);
            delete context.fn;
            return result;
        }
        let obj = {
            name: 'wy'
        }

        function say(others) {
            console.log(this.name + "-------" + others)
        }
        say.myCall(obj, 1, 2, 3, 4, 5)
2. apply
Function.prototype.myApply = function(context) {
            if(typeof this !==  'function'){
                throw new Error('error');
            }
            context = context || window;
            context.fn = this;
            let result;
            if(arguments[1]){
                result = context.fn(...arguments[1]);
            }else{
                result = context.fn();
            }
            delete context.fn;
            return result;
        }
3. bind
Function.prototype.myBind = function(context) {   // 判断调用者是否为函数
                  
                if (typeof this !== 'function') {    
                    throw new TypeError('Error') 
                }   // 截取传递的参数
                  
                const args = Array.from(arguments).slice(1)   // _this 指向调用的函数
                const _this = this;   // 返回一个函数
                console.log("我是最开始的this---------",_this)
                console.log("我是context---------",context)
                return function F() {    
                    // 因为返回了一个函数,我们可以 new F(),所以需要判断
                    // 对于 new 的情况来说,不会被任何方式改变 this
                    console.log("return---------",this)
                    if (this instanceof F) {      
                        console.log("_this-------",_this)
                        return new _this(...args, ...arguments)   
                    } else {
                        console.log("我没有new----------",_this);      
                        return _this.apply(context, args.concat(...arguments)) 
                    } 
                }
            }
            // 普通函数
        function print() {   // new 的方式调用 bind 参数输出换做 [...arguments]
              
            console.log(this.name);
        }
        // 自定义对象
        var obj = {  
                name: '小鹿'
            }
            // 调用函数的 call 方法
        let A = print.myBind(obj, 1, 2, 3);
        // 返回对象
        //let obj1 = ;
        console.log(new A());
        //console.log(A());

四、跨域

1. 为什么会存在跨域问题 ?

答:因为浏览器的同源策略限制。防止网站遭到恶意攻击,在自己服务器中通过js访问别的服务器资源,导致信息窃取

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了

如果是协议和端口造成的跨域问题“前台”是无能为力的

 	1.<img src=XXX> 
    2.<link href=XXX> 
    3.<script src=XXX>
    //可以跨域获取资源
2. 同源策略是什么?

答:浏览器规定****做请求时,请求的服务器的URL的域名必须和当前页面的URL【域名相同】浏览器才会让你做请求,(同源才才可以进行通信)——>相同包括三个方面:域名相同 协议相同 端口号相同, 同时满足三个条件才有可能出现跨域问题:1. 浏览器限制 2. 跨域 3. XHR请求???

3. 那跨域怎么解决?
答:

1.Jsonp

jsonp是一种借助于 script 标签发送跨域请求的技巧方案。script的src属性可以访问网络上的资源,并script标签可以拿到响应体。

<script>
    function test(json){
        console.log('我被调用了');
        console.log(json);
   }
</srcipt>
<script src="http://api.douban.com/v2/movie/top250?callback=test"></script>
//我们可以看到,我们预先定义了一个函数叫test,再然后在src里加了一个参数callback=test,可以发现,当请求完成,会自动调用test这个函数,并且把响应体(JSON数据)当做参数传递过来.实际上是浏览器用script的src去发起请求,并且传递一个参数叫callback=函数名,服务器接收到这个函数名,在返回的响应体里面调用这个函数,并且把json当做参数传递.
//注意:
(1)  Jsonp不是一套新技术,只是聪明的程序员想出的一套方案
(2)  能不能用这套方案,要看服务器端代码怎么写,服务器端如果写了调用函数的代码,那么就能支持JSONP方案.
(3)  如果是在JQuery中使用Ajax,则只需要在dataType属性中把json改为jsonp即可

2.CORS(跨域资源共享)

解决ajax只能同源通信的限制

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信

CORS要求浏览器(>IE10)和服务器的同时支持,是跨域的根本解决方法,由浏览器自动完成

优点在于功能更加强大支持各种HTTP Method,缺点是兼容性不如JSONP。

//前端
function createCORSRequest(method, url) {
  var xhr = new XMLHttpRequest();
  if ("withCredentials" in xhr) {
    // 此时即支持CORS的情况
    // 检查XMLHttpRequest对象是否有“withCredentials”属性
    // “withCredentials”仅存在于XMLHTTPRequest2对象里
    xhr.open(method, url, true);
  } else if (typeof != "undefined") {
    // 否则检查是否支持XDomainRequest,IE8和IE9支持
    // XDomainRequest仅存在于IE中,是IE用于支持CORS请求的方式
    xhr = new XDomainRequest();
    xhr.open(method, url);
  } else {
    // 否则,浏览器不支持CORS
    xhr = null;
  }
  return xhr;
}
var xhr = createCORSRequest('GET', url);
if (!xhr) {
  throw new Error('CORS not supported');
}
//后端
setHeader("access-control-allow-origin:*");
setHeader("access-control-allow-method:POST,GET");

3.WebSocket
Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

//前端代码: 
<div>user input:<input type="text"></div> 
 
<script src="./socket.io.js"></script> 
 
<script> 
    var socket = io('http://www.domain2.com:8080'); 
 
    // 连接成功处理 
    socket.on('connect', function() {     
 
      // 监听服务端消息   
      socket.on('message', function(msg) {         
            console.log('data from server: ---> ' + msg);      
      });    
   
      // 监听服务端关闭
      socket.on('disconnect', function() {          
            console.log('Server socket has closed.');      
      }); 
    }); 
 
    document.getElementsByTagName('input')[0].onblur = function() { 
        socket.send(this.value); 
    }; 
</script>
 
//Nodejs socket后台: 
var http = require('http'); 
var socket = require('socket.io'); 
// 启http服务 
var server = http.createServer(function(req, res) {    
     res.writeHead(200, {         
        'Content-type': 'text/html'     
     });     
     res.end(); 
}); 
server.listen('8080'); 
console.log('Server is running at port 8080...'); 
// 监听socket连接 
socket.listen(server).on('connection', function(client) {     
    // 接收信息     
    client.on('message', function(msg) {         
        client.send('hello:' + msg);         
        console.log('data from client: ---> ' + msg);     
    });     
    // 断开处理     
    client.on('disconnect', function() {         
        console.log('Client socket has closed.');      
    }); 
});

4.httpClient内部转发
实现原理很简单,若想在B站点中通过Ajax访问A站点获取结果,固然有ajax跨域问题,但在B站点中访问B站点获取结果,不存在跨域问题,这种方式实际上是在B站点中ajax请求访问B站点的HttpClient,再通过HttpClient转发请求获取A站点的数据结果。但这种方式产生了两次请求,效率低,但内部请求,抓包工具无法分析,安全。

$.ajax({
	type : "GET",
	async : false,
	url : "http://b.b.com:8080/B/FromAjaxservlet?userName=644064",
	dataType : "json",
	success : function(data) {
		alert(data["userName"]);
	},
	error : function() {
		alert('fail');
	}
});
@WebServlet("/FromAjaxservlet")
public class FromAjaxservlet extends HttpServlet{
	
	
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	    try {
			//创建默认连接
			CloseableHttpClient httpClient = HttpClients.createDefault();
			//创建HttpGet对象,处理get请求,转发到A站点
			HttpGet httpGet = new HttpGet("http://a.a.com:8080/A/FromServlet?userName="+req.getParameter("userName")); 
                    //执行
			CloseableHttpResponse response = httpClient.execute(httpGet);
			int code = response.getStatusLine().getStatusCode();
			//获取状态
			System.out.println("http请求结果为:"+code);
			if(code == 200){
                        //获取A站点返回的结果
				String result = EntityUtils.toString(response.getEntity());
				System.out.println(result);
                        //把结果返回给B站点
				resp.getWriter().print(result);
			}
			response.close();
			httpClient.close();
		} catch (Exception e) {
		}
	}
}

5.使用nginx搭建企业级接口网关方式
www.a.a.com不能直接请求www.b.b.com的内容,可以通过nginx,根据同域名,但项目名不同进行区分。什么意思呢?这么说可能有点抽象。假设我们公司域名叫www.nginxtest.com

当我们需要访问www.a.a.com通过www.nginxtest.com/A访问,并通过nginx转发到www.a.a.com

当我们需要访问www.b.b.com通过www.nginxtest.com/B访问,并通过nginx转发到www.a.a.com

我们访问公司的域名时,是"同源"的,只是项目名不同,此时项目名的作用只是为了区分,方便转发。如果你还不理解的话,先看看是怎么进行配置的:

server {
        listen       80;
        server_name  www.nginxtest.com;
        location /A {
		    proxy_pass  http://a.a.com:81;
			index  index.html index.htm;
        }
		location /B {
		    proxy_pass  http://b.b.com:81;
			index  index.html index.htm;
        }
    }
//我们访问以www.nginxtest.com开头且端口为80的网址,nginx将会进行拦截匹配,若项目名为A,则分发到a.a.com:81。实际上就是通过"同源"的域名,不同的项目名进行区分,通过nginx拦截匹配,转发到对应的网址。整个过程,两次请求,第一次请求nginx服务器,第二次nginx服务器通过拦截匹配分发到对应的网址。

6.设置document.domain解决无法读取非同源网页的 Cookie问题

因为浏览器是通过document.domain属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,两个页面就可以共享Cookie

// 两个页面都设置
document.domain = 'test.com';

7、跨文档通信 API:window.postMessage()
如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。

调用postMessage方法实现父窗口http://test1.com向子窗口http://test2.com发消息(子窗口同样可以通过该方法发送消息给父窗口)

// 父窗口打开一个子窗口
var openWindow = window.open('http://test2.com', 'title');
 
// 父窗口向子窗口发消息(第一个参数代表发送的内容,第二个参数代表接收消息窗口的url)
openWindow.postMessage('Nice to meet you!', 'http://test2.com');
//调用message事件,监听对方发送的消息
// 监听 message 消息

window.addEventListener('message', function (e) {
  console.log(e.source); // e.source 发送消息的窗口
  console.log(e.origin); // e.origin 消息发向的网址
  console.log(e.data);   // e.data   发送的消息
},false);

8.webpack代理

安装webpack-dev-server插件,然后配置proxy就OK了
通过 webpack 中的 proxy 进行代理,从而解决跨域的问题

具体看代码

配置vue.config.js文件
   module.exports = {
       devServer: {
           host: ,
           port: , // 端口号
           https: false, // https:{type:Boolean}
           open: false, //配置后自动启动浏览器
           hotOnly: false, // 热更新
           proxy: { //配置多个代理
               "": {//这里""里面的内容就是你想把target的实际接口换成什么,然后接口前面那一串你就不用写了,自动帮你配置
                   target: "",
                   open: process.platform === 'darwin',
                   changeOrigin: true,
                   ws: false,
                   secure: false,
                   pathRewrite: {
                       "^": ""
                   }
               }
           }
       }
   }

五、BFC (block formatting context)和 IFC(inline fromatting contex)

BFC是一个独立的布局环境,其中的元素布局是不受外界的影响,并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。

BFC的布局规则
  • 内部的Box会在垂直方向,一个接一个地放置。

  • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。

  • 每个盒子(块盒与行盒)的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。

  • BFC的区域不会与float box重叠。

  • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

  • 计算BFC的高度时,浮动元素也参与计算。

如何创建BFC
  1. float的值不是none。
  2. position的值不是static或者relative。
  3. display的值是inline-block、table-cell、flex、table-caption或者inline-flex
  4. overflow的值不是visible

BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

因为BFC内部的元素和外部的元素绝对不会互相影响,因此, 当BFC外部存在浮动时,它不应该影响BFC内部Box的布局,BFC会通过变窄,而不与浮动有重叠。同样的,当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动的高度。避免margin重叠也是这样的一个道理。

作用:
  1. 可以组织元素被浮动元素覆盖,实现两栏布局
  2. 可以清除浮动,解决父元素高度塌陷的问题
  3. 解决同一个bfc区域内的垂直方向上margin塌陷的问题
  • 文档流
    说BFC之前先说说文档流,文档流分为:浮动流、定位流、普通的标准流,而普通标准流其实就是BFC中的FC。

  • FC:formatting context的英文缩写,翻译过来就是格式化上下文,它是页面中的一块渲染区域,有一套渲染规则,决定了其子元素如何布局,以及与元素之间的关系和作用。

  • 常见的FC:BFC(块级格式化上下文)、IFC(行级格式上下文)、GFC(网络布局格式上下文)、和FFC(自适应格式上下文)。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-07 10:43:35  更:2021-09-07 10:43:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:45:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码