一、JavaScript类型检测的四种方法
1. typeof
ypeof 是一元运算符,同样返回一个字符串类型。一般用来判断一个变量是否为空或者是什么类型。 除了 null 类型以及 Object 类型不能准确判断外,其他数据类型都可能返回正确的类型。
typeof undefined
typeof '10'
typeof 10
typeof false
typeof Symbol()
typeof Function
typeof null
typeof []
typeof {}
面试官:为什么 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);
3. constructor
对于引用类型,除此之外,还可以使用 xx.constructor.name 构造函数来判断。
console.log(fn.constructor.name)
console.log(date.constructor.name)
console.log(arr.constructor.name)
console.log(reg.constructor.name)
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.prototype.toString.call(undefined);
Object.prototype.toString.call(“abc”);
Object.prototype.toString.call(123);
Object.prototype.toString.call(true);
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.prototype.toString.call(date);
Object.prototype.toString.call(arr);
Object.prototype.toString.call(reg);
二、this
对象调用时,谁调用,this就指向谁 通过new的方式,this永远指向新创建的对象 箭头函数没有自己的this,而是定义时在哪,所在的上下文就是它的this(找箭头函数内部的this,一层层向外剥)
可以结合这个来理解:
三、改变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)
const _this = this;
console.log("我是最开始的this---------",_this)
console.log("我是context---------",context)
return function F() {
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() {
console.log(this.name);
}
var obj = {
name: '小鹿'
}
let A = print.myBind(obj, 1, 2, 3);
console.log(new 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>
(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) {
xhr.open(method, url, true);
} else if (typeof != "undefined") {
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
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>
var http = require('http');
var socket = require('socket.io');
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.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 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){
String result = EntityUtils.toString(response.getEntity());
System.out.println(result);
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;
}
}
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');
openWindow.postMessage('Nice to meet you!', 'http://test2.com');
window.addEventListener('message', function (e) {
console.log(e.source);
console.log(e.origin);
console.log(e.data);
},false);
8.webpack代理
安装webpack-dev-server插件,然后配置proxy就OK了 通过 webpack 中的 proxy 进行代理,从而解决跨域的问题
具体看代码
配置vue.config.js文件
module.exports = {
devServer: {
host: ,
port: ,
https: false,
open: false,
hotOnly: false,
proxy: {
"": {
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
- float的值不是none。
- position的值不是static或者relative。
- display的值是inline-block、table-cell、flex、table-caption或者inline-flex
- overflow的值不是visible
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
因为BFC内部的元素和外部的元素绝对不会互相影响,因此, 当BFC外部存在浮动时,它不应该影响BFC内部Box的布局,BFC会通过变窄,而不与浮动有重叠。同样的,当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动的高度。避免margin重叠也是这样的一个道理。
作用:
- 可以组织元素被浮动元素覆盖,实现两栏布局
- 可以清除浮动,解决父元素高度塌陷的问题
- 解决同一个bfc区域内的垂直方向上margin塌陷的问题
-
文档流 说BFC之前先说说文档流,文档流分为:浮动流、定位流、普通的标准流,而普通标准流其实就是BFC中的FC。 -
FC:formatting context的英文缩写,翻译过来就是格式化上下文,它是页面中的一块渲染区域,有一套渲染规则,决定了其子元素如何布局,以及与元素之间的关系和作用。 -
常见的FC:BFC(块级格式化上下文)、IFC(行级格式上下文)、GFC(网络布局格式上下文)、和FFC(自适应格式上下文)。
|