美文网首页
前端向面试题总结精要

前端向面试题总结精要

作者: 十八人言 | 来源:发表于2020-07-09 15:56 被阅读0次

    个人表达

    1.为何选择前端这个方向?
    第一的话对互联网开发方面的东西很感兴趣,之前也接触过其他语言,但是直到接触到前端才发现很有兴趣做下去,兴趣是一个人最好的老师。
    第二的话前端的发展前景也很广阔,像现在的nodejs,微信小程序这类工具和框架可以让前端进行后端和移动开发,所以我觉得从事前端工作未来也会有很好的发展。
    2.对前端的理解?
    首先前端的核心技能还是:HTML,CSS,JS。前端负责的是用户可以看到的部分,所以也是最接近用户的工程师。同时在产品研发流程中前端要同时与产品、设计、后端等很多人合作。

    HTML/HTML5

    1.知道语义化吗?说说你理解的语义化,如果是你,平时会怎么做来保证语义化?
    像html5的新的标签header,footer,section等就是语义化
    一方面,语义化就是让计算机能够快速的读懂内容,高效的处理信息,可以对搜索引擎更友好。
    另一方面,便于与他人的协作,他人通过读代码就可以理解你网页标签的意义。
    去掉或者丢失样式的时候能够让页面呈现出清晰的结构
    有利于SEO:和搜索引擎建立良好沟通,有助于爬虫抓取更多的有效信息:爬虫依赖于标签来确定上下文和各个关键字的权重;
    方便其他设备解析(如屏幕阅读器、盲人阅读器、移动设备)以意义的方式来渲染网页;
    便于团队开发和维护,语义化更具可读性,是下一步吧网页的重要动向,遵循W3C标准的团队都遵循这个标准,可以减少差异化。
    2.介绍一下HTML5的新特性
    新的DOCTYPE声明 <!DOCTYPE html>
    完全支持css3
    video和audio
    本地存储
    语义化标签
    canvas
    新事件 如ondrag onresize
    3.如何解决ajax无法后退的问题?
    HTML5里引入了新的API,即:history.pushState, history.replaceState
    可以通过pushState和replaceState接口操作浏览器历史,并且改变当前页面的URL。
    onpopstate监听后退
    4.websocket和ajax轮询
    Websocket是HTML5中提出的新的协议,注意,这里是协议,可以实现客户端与服务器端的通信,实现服务器的推送功能。
    其优点就是,只要建立一次连接,就可以连续不断的得到服务器推送的消息,节省带宽和服务器端的压力。
    ajax轮询模拟长连接就是每个一段时间(0.5s)就向服务器发起ajax请求,查询服务器端是否有数据更新
    其缺点显而易见,每次都要建立HTTP连接,即使需要传输的数据非常少,所以这样很浪费带宽

    js

    1.什么是闭包?

    我们知道根据作用域链的规则,一个函数是不能访问到在与他同一个作用域内的函数内的内部变量的,而闭包是有权访问另一个函数作用域中的变量的函数。首先要明白的就是,闭包是函数。由于要求它可以访问另一个函数的作用于中的变量,所以我们往往是在一个函数的内部创建另一个函数,而“另一个函数”就是闭包。

    2.js这样写是什么意思?var a=(function(){})(a)

    var a=(function(a){
        // todo
    })(a);
    console.log(a);
    // 定义一个匿名函数,把a传递进去,a是undefined
    // 因为匿名函数本身没有返回值,如果是
     
    var a=(function(a){
        return 1;
    })(a);
     
    console.log(a);// a此时就是1
     
     
    // 如果要自动运行这个匿名函数的同时又赋值给a,那就应该是这样的:
    var i = 0;// 计数器
    var a=(function(a){
        i++;
        console.log("i am running..." + i);
        return arguments.callee;
    })(a);// 这里跑一次
     
    a();//被调用一次
    

    闭包适用场景

    1、模拟创建私有变量。

    function clo() {
          var foo = 'wayne';
          this.age = 22;
          this.getFoo = function () {
            return foo;
          }
          this.setFoo = function (val) {
            foo = val;
          }
        }
        var bar = new clo();
        console.log(bar.getFoo()); // wayne
        console.log(bar.setFoo('hedy')); 
        console.log(bar.getFoo()); // hedy
    

    如上所示,我们创建一个构造函数,然后想要获取其中的变量是无法直接获取的,而必须通过getFoo方法; 当然,也是无法直接设置foo值的,需要通过setFoo方法。

    2、 创建模块化,避免全局变量污染。

    比如,我们希望在一个页面中引入多个js文件,而这些js文件可能是不同的人来写的,那么不可避免的会出现重名的现象,但是如果我们在每个js文件外添加一层 (function(){})(),那么,这个js模块中的所有全局变量就成了局部变量,也就没有了全局污染的问题了。

    3、 将内部变量导出,给其他函数使用。

    比如一个内部变量无法直接被所在函数外的其他函数使用,这时,我们就可以返回一个函数,这个函数返回这个变量,这样,就可以在函数外轻松拿到这个变量了。

    网络请求

    1.http响应中content-type包含哪些内容?
    Content-Type用于指定内容类型,一般是指网页中存在的Content-Type,Content-Type属性指定请求和响应的HTTP内容类型。如果未指定 ContentType,默认为text/html。
    在nginx中有个配置文件mime.types,主要是标示Content-Type的文件格式。
    下面是几个常见的Content-Type:
    1.text/html
    2.text/plain
    3.text/css
    4.text/javascript
    5.application/x-www-form-urlencoded
    6.multipart/form-data
    7.application/json
    8.application/xml
    前面几个都很好理解,都是html,css,javascript的文件类型,后面四个是POST的发包方式。

    2.1.application/x-www-form-urlencoded

    application/x-www-form-urlencoded是常用的表单发包方式,普通的表单提交,或者js发包,默认都是通过这种方式,

    比如一个简单地表单:

    <form enctype="application/x-www-form-urlencoded" action="http://homeway.me/post.php" method="POST">
        <input type="text" name="name" value="homeway">
        <input type="text" name="key" value="nokey">
        <input type="submit" value="submit">
    </form>
    

    那么服务器收到的raw header会类似:

    Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Accept-Encoding:gzip, deflate
    Accept-Language:zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,gl;q=0.2,de;q=0.2
    Cache-Control:no-cache
    Connection:keep-alive
    Content-Length:17
    Content-Type:application/x-www-form-urlencoded
    

    那么服务器收到的raw body会是,name=homeway&key=nokey,在php中,通过$_POST就可以获得数组形式的数据。

    2.2 multipart/form-data

    multipart/form-data用在发送文件的POST包。

    这里假设我用python的request发送一个文件给服务器:

    data = {
        "key1": "123",
        "key2": "456",
    }
    
    files = {'file': open('index.py', 'rb')}
    res = requests.post(url="http://localhost/upload", method="POST", data=data, files=files)
    print res
    

    通过工具,可以看到我发送的数据内容如下:

    POST http://www.homeway.me HTTP/1.1
    Content-Type:multipart/form-data; boundary=------WebKitFormBoundaryOGkWPJsSaJCPWjZP
    
    ------WebKitFormBoundaryOGkWPJsSaJCPWjZP
    Content-Disposition: form-data; name="key2"
    456
    ------WebKitFormBoundaryOGkWPJsSaJCPWjZP
    Content-Disposition: form-data; name="key1"
    123
    ------WebKitFormBoundaryOGkWPJsSaJCPWjZP
    Content-Disposition: form-data; name="file"; filename="index.py"
    

    这里Content-Type告诉我们,发包是以multipart/form-data格式来传输,另外,还有boundary用于分割数据。

    当文件太长,HTTP无法在一个包之内发送完毕,就需要分割数据,分割成一个一个chunk发送给服务端,

    那么--用于区分数据快,而后面的数据就是标示区分包作用。

    2.3 text/xml

    微信用的是这种数据格式发送请求的。

    POST http://www.homeway.me HTTP/1.1 
    Content-Type: text/xml
    
    <?xml version="1.0"?>
    <resource>
        <id>123</id>
        <params>
            <name>
                <value>homeway</value>
            </name>
            <age>
                <value>22</value>
            </age>
        </params>
    </resource>
    

    php中_POST只能读取application/x-www-form-urlencoded数据,$_FILES只能读取multipart/form-data类型数据,

    那么,要读取text/xml格式的数据,可以用:

    $file = fopen('php://input', 'rb');
    $data = fread($file, length);
    fclose($file);
    

    或者

    $data = file_get_contents('php://input');
    

    2.4 application/json

    通过json形式将数据发送给服务器,一开始,我尝试通过curl,给服务器发送application/json格式包,

    然而我收到的数据如下:

    ---------e1e1406176ee348a Content-Disposition: form-data; name="nid" 2---------------
    ---------e1e1406176ee348a Content-Disposition: form-data; name="uuid" cf9dc994-a4e7-3ad6-bc54-41965b2a0dd7-----------
    ----------e1e1406176ee348a Content-Disposition: form-data; name="access_token" 956731586df41229dbfec08dd5d54eedb98d73d2----------
    ----------e1e1406176ee348a--
    

    HTTP通信中并不存在所谓的json,而是将string转成json罢了,也就是,application/json可以将它理解为text/plain,普通字符串。

    3.如何进行用户认证?

    前端登录,后端根据用户信息生成一个jsessionid,并保存这个 jsessionid 和对应的用户id到Session中,接着把 jsessionid 传给用户,存入浏览器 cookie,之后浏览器请求带上这个cookie,后端根据这个cookie值来查询用户,验证是否过期。

    HTTP有一个特性:无状态的,就是前后两个HTTP事务它们并不知道对方的信息。而为了维护会话信息或用户信息,一般可用Cookie和Session技术缓存信息。

    • Cookie是存储在客户端的

    • Session是存储在服务端的

    但这样做问题就很多,如果我们的页面出现了 XSS 漏洞,由于 cookie 可以被 JavaScript 读取,XSS 漏洞会导致用户 token 泄露,而作为后端识别用户的标识,cookie 的泄露意味着用户信息不再安全。尽管我们通过转义输出内容,使用 CDN 等可以尽量避免 XSS 注入,但谁也不能保证在大型的项目中不会出现这个问题。

    在设置 cookie 的时候,其实你还可以设置 httpOnly 以及 secure 项。设置 httpOnly 后 cookie 将不能被 JS 读取,浏览器会自动的把它加在请求的 header 当中,设置 secure 的话,cookie 就只允许通过 HTTPS 传输。secure 选项可以过滤掉一些使用 HTTP 协议的 XSS 注入,但并不能完全阻止。

    httpOnly 选项使得 JS 不能读取到 cookie,那么 XSS 注入的问题也基本不用担心了。但设置 httpOnly 就带来了另一个问题,就是很容易的被 XSRF,即跨站请求伪造。当你浏览器开着这个页面的时候,另一个页面可以很容易的跨站请求这个页面的内容。因为 cookie 默认被发了出去。

    浏览器

    1.浏览器缓存有哪些,通常缓存有哪几种方式

    浏览器缓存(Browser Caching)是浏览器端保存数据用于快速读取或避免重复资源请求的优化机制,有效的缓存使用可以避免重复的网络请求和浏览器快速地读取本地数据,整体上加速网页展示给用户。浏览器端缓存的机制种类较多,总体归纳为九种,这里详细分析下这九种缓存机制的原理和使用场景。打开浏览器的调试模式->resources左侧就有浏览器的8种缓存机制。

    一、http缓存

    http缓存是基于HTTP协议的浏览器文件级缓存机制。即针对文件的重复请求情况下,浏览器可以根据协议头判断从服务器端请求文件还是从本地读取文件,chrome控制台下的Frames即展示的是浏览器的http文件级缓存。以下是浏览器缓存的整个机制流程。主要是针对重复的http请求,在有缓存的情况下判断过程主要分3步:

    • 判断expires,如果未过期,直接读取http缓存文件,不发http请求,否则进入下一步
    • 判断是否含有etag,有则带上if-none-match发送请求,未修改返回304,修改返回200,否则进入下一步
    • 判断是否含有last-modified,有则带上if-modified-since发送请求,无效返回200,有效返回304,否则直接向服务器请求
      如果通过etag和last-modified判断,即使返回304有至少有一次http请求,只不过返回的是304的返回内容,而不是文件内容。所以合理设计实现expires参数可以减少较多的浏览器请求。

    二、websql

    websql这种方式只有较新的chrome浏览器支持,并以一个独立规范形式出现,主要有以下特点

    • Web Sql 数据库API 实际上不是HTML5规范的组成部分;
    • 在HTML5之前就已经存在了,是单独的规范;
    • 它是将数据以数据库的形式存储在客户端,根据需求去读取;
    • 跟Storage的区别是: Storage和Cookie都是以键值对的形式存在的;
    • Web Sql 更方便于检索,允许sql语句查询;
    • 让浏览器实现小型数据库存储功能;
    • 这个数据库是集成在浏览器里面的,目前主流浏览器基本都已支持;

    websql API主要包含三个核心方法:

    • openDatabase : 这个方法使用现有数据库或创建新数据库创建数据库对象。
    • transaction : 这个方法允许我们根据情况控制事务提交或回滚。
    • executeSql : 这个方法用于执行真实的SQL查询。

    openDatabase方法可以打开已经存在的数据库,不存在则创建

        var db = openDatabase('mydatabase', '2.0', my db', 2 * 1024);
    

    openDatabasek中五个参数分别为:数据库名、版本号、描述、数据库大小、创建回调。创建回调没有也可以创建数据库。

    database.transaction() 函数用来查询,executeSql()用于执行sql语句

    例如在mydatabase数据库中创建表t1:

      var db = openDatabase(' mydatabase ', '1.0', 'Test DB', 2 * 1024 * 1024);  
       db.transaction(function (tx) {    
          tx.executeSql('CREATE TABLE IF NOT EXISTS t1 (id unique, log)');  
       });
    

    插入操作

    var db = openDatabase('mydatabase', '2.0', my db', 2 * 1024);
       db.transaction(function (tx) { 
          tx.executeSql('CREATE TABLE IF NOT EXISTS t1 (id unique, log)');  
          tx.executeSql('INSERT INTO t1 (id, log) VALUES (1, "foobar")');  
          tx.executeSql('INSERT INTO t1 (id, log) VALUES (2, "logmsg")');  
       });
    

    在插入新记录时,我们还可以传递动态值,如:

      var db = openDatabase(' mydatabase ', '2.0', 'my db', 2 * 1024);  
       db.transaction(function (tx) {    
         tx.executeSql('CREATE TABLE IF NOT EXISTS t1 (id unique, log)');  
         tx.executeSql('INSERT INTO t1 (id,log) VALUES (?, ?'), [e_id, e_log];  //e_id和e_log是外部变量
       });
    

    读操作,如果要读取已经存在的记录,我们使用一个回调捕获结果:

       var db = openDatabase(mydatabase, '2.0', 'my db', 2*1024);     
          db.transaction(function (tx) { 
          tx.executeSql('CREATE TABLE IF NOT EXISTS t1 (id unique, log)');  
          tx.executeSql('INSERT INTO t1 (id, log) VALUES (1, "foobar")');  
          tx.executeSql('INSERT INTO t1 (id, log) VALUES (2, "logmsg")');  
       });  
       db.transaction(function (tx) { 
          tx.executeSql('SELECT * FROM t1, [], function (tx, results) { 
          var len = results.rows.length, i;  
          msg = "<p>Found rows: " + len + "</p>";  
          document.querySelector('#status').innerHTML +=  msg;  
          for (i = 0; i < len; i++){ 
             alert(results.rows.item(i).log );  
          } 
        }, null);  
       });
    

    三、indexDB

    IndexedDB 是一个为了能够在客户端存储可观数量的结构化数据,并且在这些数据上使用索引进行高性能检索的 API。虽然 DOM 存储 对于存储少量数据是非常有用的,但是它对大量结构化数据的存储就显得力不从心了。IndexedDB 则提供了这样的一个解决方案。  IndexedDB 分别为同步和异步访问提供了单独的 API 。同步 API 本来是要用于仅供 Web Workers 内部使用,但是还没有被任何浏览器所实现。异步 API 在 Web Workers 内部和外部都可以使用,另外浏览器可能对indexDB有50M大小的限制,一般用户保存大量用户数据并要求数据之间有搜索需要的场景。

    • 异步API
        异步 API 方法调用完后会立即返回,而不会阻塞调用线程。要异步访问数据库,要调用 window 对象 indexedDB 属性的 open() 方法。该方法返回一个 IDBRequest 对象 (IDBOpenDBRequest);异步操作通过在 IDBRequest 对象上触发事件来和调用程序进行通信。
    • IDBFactory 提供了对数据库的访问。这是由全局对象 indexedDB 实现的接口,因而也是该 API 的入口。- IDBCursor 遍历对象存储空间和索引。- IDBCursorWithValue 遍历对象存储空间和索引并返回游标的当前值。- IDBDatabase 表示到数据库的连接。只能通过这个连接来拿到一个数据库事务。- IDBEnvironment 提供了到客户端数据库的访问。它由 window 对象实现。- IDBIndex 提供了到索引元数据的访问。- IDBKeyRange 定义键的范围。- IDBObjectStore 表示一个对象存储空间。- IDBOpenDBRequest 表示一个打开数据库的请求。-IDBRequest 提供了到数据库异步请求结果和数据库的访问。这也是在你调用一个异步方法时所得到的。- IDBTransaction 表示一个事务。你在数据库上创建一个事务,指定它的范围(例如你希望访问哪一个对象存储空间),并确定你希望的访问类型(只读或写入)。- IDBVersionChangeEvent 表明数据库的版本号已经改变。
    • 同步API
        规范里面还定义了 API 的同步版本。同步 API 还没有在任何浏览器中得以实现。它原本是要和 webWork 一起使用的。

    四、cookie

    Cookie(或者Cookies),指一般网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。cookie一般通过http请求中在头部一起发送到服务器端。一条cookie记录主要由键、值、域、过期时间、大小组成,一般用户保存用户的认证信息。cookie最大长度和域名个数由不同浏览器决定,具体如下:

    浏览器 支持域名个数 最大长度
    IE7以上 50个 4095B
    Firefox 50个 4097B
    Opera 30个 4096B
    Safari/WebKit 无限制 4097B

    不同域名之间的cookie信息是独立的,如果需要设置共享可以在服务器端设置cookie的path和domain来实现共享。浏览器端也可以通过document.cookie来获取cookie,并通过js浏览器端也可以方便地读取/设置cookie的值。

    五、localstorage

    localStorage是html5的一种新的本地缓存方案,目前用的比较多,一般用来存储ajax返回的数据,加快下次页面打开时的渲染速度。

    浏览器 最大长度
    IE8以上 5M
    Firefox 8以上 5.24M
    Opera 2M
    Safari/WebKit 2.6M
    //localStorage核心API:localStorage.setItem(key, value)    
    //设置记录localStorage.getItem(key)            
    //获取记录localStorage.removeItem(key)       
    //删除该域名下单条记录localStorage.clear()                
    //删除该域名下所有记录
    

    值得注意的是,localstorage大小有限制,不适合存放过多的数据,如果数据存放超过最大限制会报错,并移除最先保存的数据。

    六、sessionstorage

    sessionStorage和localstorage类似,但是浏览器关闭则会全部删除,api和localstorage相同,实际项目中使用较少。

    七、application cache

    application cahce是将大部分图片资源、js、css等静态资源放在manifest文件配置中。当页面打开时通过manifest文件来读取本地文件或是请求服务器文件。  离线访问对基于网络的应用而言越来越重要。虽然所有浏览器都有缓存机制,但它们并不可靠,也不一定总能起到预期的作用。HTML5 使用ApplicationCache 接口可以解决由离线带来的部分难题。前提是你需要访问的web页面至少被在线访问过一次。  使用缓存接口可为您的应用带来以下三个优势:

    • 离线浏览 – 用户可在离线时浏览您的完整网站
    • 速度 – 缓存资源为本地资源,因此加载速度较快。
    • 服务器负载更少 – 浏览器只会从发生了更改的服务器下载资源。

    一个简单的离线页面主要包含以下几个部分:

    index.html

    <html manifest="clock.manifest">
      <head>
        <title>AppCache Test</title>
        <link rel="stylesheet" href="clock.css">
        <script src="clock.js"></script>
      </head>
      <body>
        <p><output id="clock"></output></p>
        <div id="log"></div>
      </body>
    </html>
    

    clock.manifest

    CACHE MANIFEST
    #VERSION 1.0
    CACHE:
    clock.css
    clock.js
    

    clock.js和clock.css为独立的另外文件。  另外需要注意的是更新缓存。在程序中,你可以通过window.applicationCache 对象来访问浏览器的app cache。你可以查看 status 属性来获取cache的当前状态:

    var appCache = window.applicationCache;
    switch (appCache.status) {
      case appCache.UNCACHED: // UNCACHED == 0
        return 'UNCACHED';
        break;
      case appCache.IDLE: // IDLE == 1
        return 'IDLE';
        break;
      case appCache.CHECKING: // CHECKING == 2
        return 'CHECKING';
        break;
      case appCache.DOWNLOADING: // DOWNLOADING == 3
        return 'DOWNLOADING';
        break;
      case appCache.UPDATEREADY:  // UPDATEREADY == 4
        return 'UPDATEREADY';
        break;
      case appCache.OBSOLETE: // OBSOLETE == 5
        return 'OBSOLETE';
        break;
      default:
        return 'UKNOWN CACHE STATUS';
        break;
    };
    

    为了通过编程更新cache,首先调用 applicationCache.update()。这将会试图更新用户的 cache(要求manifest文件已经改变)。最后,当 applicationCache.status 处于 UPDATEREADY 状态时, 调用applicationCache.swapCache(),旧的cache就会被置换成新的。

    var appCache = window.applicationCache;
    appCache.update(); // Attempt to update the user’s cache.
    …
    if (appCache.status == window.applicationCache.UPDATEREADY) {
      appCache.swapCache();  // The fetch was successful, swap in the new cache.
    }
    

    这里是通过更新menifest文件来控制其它文件更新的。

    八、cacheStorage

    CacheStorage是在ServiceWorker的规范中定义的。CacheStorage 可以保存每个serverWorker申明的cache对象,cacheStorage有open、match、has、delete、keys五个核心方法,可以对cache对象的不同匹配进行不同的响应。

    cacheStorage.has()如果包含cache对象,则返回一个promise对象。cacheStorage.open()打开一个cache对象,则返回一个promise对象。cacheStorage.delete()删除cache对象,成功则返回一个promise对象,否则返回false。cacheStorage.keys()含有keys中字符串的任意一个,则返回一个promise对象。cacheStorage.delete()匹配key中含有该字符串的cache对象,返回一个promise对象。

    caches.has('v1').then(function() {
      caches.open('v1').then(function(cache) {
          return cache.addAll(myAssets);
      });
    }).catch(function() {
      someCacheSetupfunction();
    });;
    
    var response;
    var cachedResponse = caches.match(event.request).catch(function() {
      return fetch(event.request);
    }).then(function(r) {
      response = r;
      caches.open('v1').then(function(cache) {
        cache.put(event.request, response);
      });  
      return response.clone();
    }).catch(function() {
      return caches.match('/sw-test/gallery/myLittleVader.jpg');
    });
    
    then.addEventListener('activate', function(event) {
      var cacheWhitelist = ['v2'];
     
      event.waitUntil(
        caches.keys().then(function(keyList) {
          return Promise.all(keyList.map(function(key) {
            if (cacheWhitelist.indexOf(key) === -1) {
              return caches.delete(keyList[i]);
            }
          });
        })
      );
    });
    

    九、flash缓存

    这种方式基本不用,这一方法主要基于flash有读写浏览器端本地目录的功能,同时也可以向js提供调用的api,则页面可以通过js调用flash去读写特定的磁盘目录,达到本地数据缓存的目的。

    注释PS

    • Web Storage / Web SQL Database / Indexed Database 的数据都存储在浏览器对应的用户配置文件目录(user profile directory)下,以 Windows 7 为例,Chrome 的数据存储在”C:\Users\your-account-name\AppData\Local\Google\Chrome\User Data\Default\”下,而 Firefox 的数据存储在”C:\Users\your-account-name\AppData\Local\Mozilla\Firefox\Profiles\”目录下。
    • cookie文件存储于documents and settings\userName\cookie\文件夹下。通常的命名格式为:userName@domain.txt。
    • 较多的缓存机制目前主流浏览器并不兼容,不过可以使用polyfill的方法来处理

    浏览器涉及的缓存方式主要包含这些,具体结合自己的业务场景进行选择使用

    相关文章

      网友评论

          本文标题:前端向面试题总结精要

          本文链接:https://www.haomeiwen.com/subject/aztwwctx.html