每天一点面试题(9) ------记一次面试经验

什么是纯函数以及为什么要用纯函数?

在这里插入图片描述

什么函数是纯的?
  1. 如果函数的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化,必须只依赖于其输入参数。

  2. 该函数不会产生任何可观察的副作用,例如网络请求,输入和输出设备或数据突变(mutation)。

这就是纯的函数。 如果一个函数符合上述 2 个要求,它就是纯函数。 你可能在过去甚至无意地情况下编写过纯函数。
在我们研究一个函数一个纯或不纯之前,让我们先讨论一下可怕的“副作用”。

什么是可观察的副作用?

一个可以被观察的副作用是在函数内部与其外部的任意交互。这可能是在函数内修改外部的变量,或者在函数里调用另外一个函数等。

注: 如果纯函数调用纯函数,则不产生副作用依旧是纯函数。

副作用来自,但不限于:
进行一个 HTTP 请求
Mutating data
输出数据到屏幕或者控制台
DOM 查询/操作
Math.random()
获取的当前时间
副作用本身并不是毒药,某些时候往往是必需的。 但是,对于要保持纯粹的函数,它不能包含任何副作用。当然,并非所有函数都需要是纯函数。 我将在稍后讨论这个情况。

不过首先,让我们来看一些纯的和不纯的函数对比的例子…

纯函数的例子

以下是一个计算产品税后价格(英国税率是20%)的纯函数的例子:

	function priceAfterTax(productPrice) { 
		return (productPrice * 0.20) + productPrice;
	}

它符合我们所说的两条纯函数的定义。不依赖于任何外部输入,不改变任何外部数据、没有副作用。

即使你用同样的输入运行运行这个函数 100,000,000 次它依旧产生同样的结果。

非纯函数的例子

我们已经看了纯函数的例子,现在一起来看一个非纯函数(Impure function)的 JavaScript 例子:

var tax = 20;
function calculateTax(productPrice) { 
	return (productPrice * (tax/100)) + productPrice;
}

暂停片刻,看看你是否能看出为什么这个函数不纯。

其中函数的计算结果取决于外部 tax 变量,而纯函数不能依赖外部变量。它没有满足定义中的第一个要求,因此这个函数是不纯的。

为什么说纯函数在 JavaScript 很重要?

纯函数在函数式编程中被大量使用。而且诸如 ReactJS 和 Redux 等优质的库都需要使用纯函数。

不过,纯函数也可以用在平常的 JavaScript 开发中使用,不一定要限死在某个编程范例中。 你可以混合纯的和不纯的函数,这完全没问题。

并非所有函数都需要是纯的。 例如,操作 DOM 的按钮按下的事件处理程序就不适合纯函数。 不过,这种事件处理函数可以调用其他纯函数来处理,以此减少项目中不纯函数的数量。

ES6中map和set数据结构及用法

Map是一组键值对的结构,具有极快的查找速度

举个例子,假设要根据同学的名字查找对应的成绩,如果用Array实现,需要两个Array;

    var names = ['Michael', 'Bob', 'Tracy'];
    var scores = [95, 75, 85];

给定一个名字,要查找对应的成绩,就先要在names中找到对应的位置,再从scores取出对应的成绩,Array越长,耗时越长。

或者通过object键值对的方式来实现存储和查找:

var names = Object.create({'Michael':95,'Bob':75,'Tracy':85});

所以ES6提供了map方法来提高查询的速度,如果用Map实现,只需要一个“名字”-“成绩”的对照表,直接根据名字查找成绩,无论这个表有多大,查找速度都不会变慢。

    var m=new Map();
    m.set('Adam', 67); // 添加新的key-value
    m.set('Bob', 59);
    console.log(m);//Map { 'Adam' => 67, 'Bob' => 59 }
    console.log(m.has('Adam')); // 是否存在key 'Adam': true
    console.log(m.get('Adam')); // 67
    console.log(m.delete('Adam')); // 删除key 'Adam':true
    console.log(m.get('Adam')); // undefined
ES6中Map相对于Object对象有几个区别:

1.Object对象有原型,也就是他有默认的key值在对象上面,除非我们使用Object.create(null)创建一个没有原型的对象;

2.在Object对象中,只能把String和Symbol作为key值,但是在Map中,key值可以是任何基本数据类型(String,Number,Boolean,undefined,NaN…)或者对象(Map,Set,Object,Function,Symbol,null…);

3.通过Map中的size属性,可以很方便的获取到Map长度

二、set

Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key

要创建一个Set,需要提供一个Array作为输入;

    var arr=[1,2,3,3,'3'];
    var s1=new Set(arr);
    console.log(s1);//Set { 1, 2, 3, '3' }
    s1.add(4);//重复添加无用
    console.log(s1);//Set { 1, 2, 3, '3', 4 }
    s1.delete(4);
    console.log(s1);//Set { 1, 2, 3, '3' }

感觉set的用法可以有两个地方

因为key不能重复,所以可以用来去掉array中的重复元素。

    'use strict';
    // var set = new Set([1,2,1,2,2,1]);
    var arr = [1,2,1,2,2,1];
    //new Set 数组去重
    function unique(arr){
    return Array.from(new Set(arr));
    };
    //使用ES6的方法可以去重.
    console.log(unique(arr));

跨域 CORS的原理

跨域问题的解决方案

修改浏览器的设置
修改请求的方式:jsonp
CORS

修改浏览器配置解决跨域

以Google Chrome为例,浏览器以

    "C:\ProgramFiles(x86)\Google\Chrome\Application\chrome.exe"
        --disable-web-security--user-data-dir

中模式打开,右键点击浏览器快捷方式,在目标中输入上述代码即可解决(不推荐)。

使用jsonp解决跨域

JQuery中的正常AJAX请求代码片段

$("#demo1").click(function(){
    $.ajax({
        url : 'http://www.tpadmin.top/Index/Test/crossDomain',
        data : {},
        type : 'get',
        success : function (res) {
            //No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1' is therefore not allowed access. 在执行时候报出的错误,这代表了跨域错误
            alert(res);
        }
    });
});

在这里插入图片描述 JQuery中的使用JSONP的AJAX请求代码:

$("#demo2").click(function(){
    $.ajax({
        url : 'http://www.tpadmin.top/Index/Test/crossDomain',
        data : {},
        type : 'get',
        dataType : 'jsonp', 
        success : function (res) {
            alert(res);
        }
    });
});

在这里插入图片描述这时候我们看到 请求的网址自动变成了

http://www.tpadmin.top/Index/Test/crossDomain?callback=jQuery331015214102388989237_1534993962395&_=1534993962396

这是为什么呢?原来由于跨域访问的只限制xhr类型的请求(上文中已经说了),所以js中就利用了这一特点,让服务端不在返回的是一个JSON格式的数据,而是返回一段JS代码,将JSON的数据以参数的形式传递到这个函数中,而函数的名称就是callback参数的值,所以我们还需要修改服务端的代码,代码如下:

<?php
    $callback = isset($_GET['callback'])?$_GET['callback']:'';
    if (!empty($callback)) {
        $arr = ['code' => 200, 'name' => 'cui'];
        $data = json_encode($arr);
        exit($callback . '(' . $data . ')');
    }
?>

在这里插入图片描述OK,现在问题解决了,但是JSONP存在着诸多限制,下面将列出两个个我知道的:

JSONP只支持GET请求,什么?你要提交表单,sorry,此路不通
它只支持跨域HTTP请求

虽然只有两个,但是让很多人不得不放弃它,所以出现了下面的解决办法。

CORS解决跨域

回归问题本质,跨域问题为什么会产生,上面已经说了,是由于浏览器的限制,那么在执行过程中有什么不同,下面两张度分析一下(主要看请求头的部分):

这是非跨域请求
在这里插入图片描述在这里插入图片描述这时我们发现跨域访问的请求头中存在Origin的字段,用来记录当前的访问域名,我们可以再服务端增加一个响应头Access-Control-Allow-Origin来告诉浏览器我们支持它获取就可以了,代码实现:

<?php
header('Access-Control-Allow-Origin:http://127.0.0.1');
$arr = ['code' => 200, 'name' => 'cui'];
echo $data = json_encode($arr);
?>

那如果我有多个域名进行跨域访问呢

<?php
$requestHeader = getallheaders();
$origin = isset($requestHeader['Origin'])?$requestHeader['Origin']:'';
switch ($origin) {
    case 'http://127.0.0.1':
        header('Access-Control-Allow-Origin:http://127.0.0.1');
        break;
    case 'http://localhost':
        header('Access-Control-Allow-Origin:http://localhost');
        break;
    default:
        break;
}
$arr = ['code' => 200, 'name' => 'cui'];
echo $data = json_encode($arr);
//注意,不支持下面这种写法
//header('Access-Control-Allow-Origin:http://localhost,http://127.0.0.1');
?>

或者直接写成(很不安全,不推荐这么写)

<?php
header('Access-Control-Allow-Origin:*');
$arr = ['code' => 200, 'name' => 'cui'];
echo $data = json_encode($arr);
?>

到这里,其实已经结束了,但还有一些其他的特殊情况

请求方法不是GET、HEAD、POST
请求头中存在自定义头
Content-Type不是text/plain、multipart/form-data、application/x-www-form-urlencoded
希望获取到服务端的Cookie

为了应对种种限制,我们再来看一段代码

$("#demo1").click(function(){
    $.ajax({
        url : 'http://cui.tpadmin.top/crossDomain.php',
        data : {},
        type : 'PUT',
        contentType : 'application/json',
        header: {
            token:'asdfgqwerttyyazxcvbvb'
        },
        success : function (res) {
            alert(res);
        }
    });
});

在这里插入图片描述虽然我们在服务端加入了Access-Control-Allow-Origin响应头,但是如果出现上面所说的情况时,我们需要做一些特殊的设置,修改服务端代码为:

<?php
//这里增加了两行代码
header('Access-Control-Allow-Headers:Content-Type');
header('Access-Control-Allow-Methods:PUT');
$requestHeader = getallheaders();
$origin = isset($requestHeader['Origin'])?$requestHeader['Origin']:'';
switch ($origin) {
    case 'http://127.0.0.1':
        header('Access-Control-Allow-Origin:http://127.0.0.1');
        break;
    case 'http://localhost':
        header('Access-Control-Allow-Origin:http://localhost');
        break;
    default:
        break;
}
$arr = ['code' => 200, 'name' => 'cui'];
echo $data = json_encode($arr);
?>

在这里插入图片描述
这里虽然成功了,但是我们发现每次请求的时候会出现两条请求记录(这个可不是我请求了两次,看下面截图)在这里插入图片描述在这里插入图片描述这里我们需要进行一下区分(简单请求模式与非简单请求模式)

请求方法只能为GET、HEAD、POST
请求头中无自定义头
Content-Type必须为text/plain、multipart/form-data、application/x-www-form-urlencoded

符合以上条件的为简单请求,否则为非简单请求,注意,非简单请求中,浏览器会默认发送两条请求,第一条为预检请求(OPTION),第二条为AJAX的请求,处于服务器的性能考量,我们需要将预检命令进行缓存,而不是每次都执行预检请求,我们可以修改代码如下

<?php
header('Access-Control-Allow-Headers:Content-Type');
header('Access-Control-Allow-Methods:PUT');
//看这里
header('Access-Control-Max-Age:3600');
$requestHeader = getallheaders();
$origin = isset($requestHeader['Origin'])?$requestHeader['Origin']:'';
switch ($origin) {
    case 'http://127.0.0.1':
        header('Access-Control-Allow-Origin:http://127.0.0.1');
        break;
    case 'http://localhost':
        header('Access-Control-Allow-Origin:http://localhost');
        break;
    default:
        break;
}
$arr = ['code' => 200, 'name' => 'cui'];
echo $data = json_encode($arr);
?>

在这里插入图片描述这次我请求了两次,发现第二次请求没有发送预检请求,新增加的代码代表允许缓存的时间(3600S)。

另外还有一些常用的方法,我都放到这里了,有需要的小伙伴可以参考

<?php
//支持Cookie
header('Access-Control-Allow-Credentials:true');
//支持自定义头
header('Access-Control-Allow-Headers:token,Content-Type,...')

由于篇幅有限,Cookie的例子小伙伴们自己做就可以了,使用Cookie的时候需要注意Access-Control-Allow-Origin不可设置为*。

服务软件实现跨域

上面的例子实现了跨域访问,但是如果我不想在代码中修改,还有其他的方法吗,当然有了,我们可以直接修改服务软件,下面将从最常用的Apache与Nignx两个方面说明
基于Apache的服务

<VirtualHost *:80>
  ServerName localhost
  ServerAlias localhost
  DocumentRoot "${INSTALL_DIR}/www"
  
  Header always set Access-Control-Allow-Header "PUT"
  Header always set Access-Control-Allow-Credentials "true"
  Header always set Access-Control-Max-Age "3600"
  #这里设置的为全匹配
  Header always set Access-Control-Allow-Origin "expr=%{req:origin}"
  #这里设置的为全匹配
  Header always set Access-Control-Allow-Headers "expr=%{req:Access-Control-Allow-Headers}"
  
  <Directory "${INSTALL_DIR}/www/">
    Options +Indexes +Includes +FollowSymLinks +MultiViews
    AllowOverride All
    Require local
  </Directory>
</VirtualHost> 

这里说明一下,由于Apache都是模块管理,所以这里要想使用Header的话,需要加载mod_headers.so这个模块,去看一下自己的主配置文件,这个模块是否开启,如果没有开启,apache会启动失败的。

LoadModule headers_module modules/mod_headers.so

基于Nignx的服务
在nignx服务的配置中修改为如下代码(注意位置,这个例子是修改本地的nignx,没有域名,如果没有熟悉过nignx的小伙伴可以自行百度)。

server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  _;
    root         /usr/share/nginx/html;
    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;
    location / {
            #支持其他请求
            add_header Access-Control-Allow-Methods PUT;
            #设置预检请求的缓存
            add_header Access-Control-Max-Age 3600;
            #允许Cookie
            add_header Access-Control-Allow-Credentials true;
            #这里最好做判断,怕麻烦的话就写*,但是不建议
            if ($http_origin = http://localhost){
                    add_header Access-Control-Allow-Origin http://localhost;
            }
            if ($http_origin = http://127.0.0.1){
                    add_header Access-Control-Allow-Origin http://127.0.0.1;
            }
            #为了方便,这样写了
            add_header Access-Control-Allow-Headers $http_access_control_request_headers;
            if ($request_method = OPTIONS){
                    return 200;
            }
    }
    error_page 404 /404.html;
        location = /40x.html {
    }
    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
}
发布了38 篇原创文章 · 获赞 1 · 访问量 553

猜你喜欢

转载自blog.csdn.net/weixin_43718291/article/details/103389894