• 前端知识


    编写javascript深度克隆函数deepClone

    function deepClone(obj) {
        var _toString = Object.prototype.toString;

        // null, undefined, non-object, function
        if (!obj || typeof obj !== 'object') {
            return obj;
        }

        // DOM Node
        if (obj.nodeType && 'cloneNode' in obj) {
            return obj.cloneNode(true);
        }

        // Date
        if (_toString.call(obj) === '[object Date]') {
            return new Date(obj.getTime());
        }

        // RegExp
        if (_toString.call(obj) === '[object RegExp]') {
            var flags = [];
            if (obj.global) { flags.push('g'); }
            if (obj.multiline) { flags.push('m'); }
            if (obj.ignoreCase) { flags.push('i'); }

            return new RegExp(obj.source, flags.join(''));
        }

        var result = Array.isArray(obj) ? [] :
            obj.constructor ? new obj.constructor() : {};

        for (var key in obj ) {
            result[key] = deepClone(obj[key]);
        }

        return result;
    }

    function A() {
        this.a = a;
    }

    var a = {
        name: 'qiu',
        birth: new Date(),
        pattern: /qiu/gim,
        container: document.body,
        hobbys: ['book', new Date(), /aaa/gim, 111]};

    var c = new A();
    var b = deepClone(c);
    console.log(c.a === b.a);
    console.log(c, b);


    补充代码,鼠标单击Button1后将Button1移动到Button2的后面

    <!doctype html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>TEst</title>
    </head>
    <body>

    <div>
      <input type="button" id ="button1" value="1" />
      <input type="button" id ="button2" value="2" />
    </div>

    <script type="text/javascript">
        var btn1 = document.getElementById('button1');
        var btn2 = document.getElementById('button2');

        addListener(btn1, 'click', function (event) {
            btn1.parentNode.insertBefore(btn2, btn1);
        });

        function addListener(elem, type, handler) {
            if (elem.addEventListener) {
                elem.addEventListener(type, handler, false);
                return handler;
            } else if (elem.attachEvent) {
                function wrapper() {
                    var event = window.event;
                    event.target = event.srcElement;
                    handler.call(elem, event);
                }
                elem.attachEvent('on' + type, wrapper);
                return wrapper;
            }
        }

    </script>
    </body>
    </html>


    网页中实现一个计算当年还剩多少时间的倒数计时程序,要求网页上实时动态显示"××年还剩××天××时××分××秒"

    <!doctype html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>TEst</title>
    </head>
    <body>

        <span id="target"></span>


    <script type="text/javascript">
        // 为了简化。每月默认30天
        function getTimeString() {
            var start = new Date();
            var end = new Date(start.getFullYear() + 1, 0, 1);
            var elapse = Math.floor((end - start) / 1000);

            var seconds = elapse % 60 ;
            var minutes = Math.floor(elapse / 60) % 60;
            var hours = Math.floor(elapse / (60 * 60)) % 24;
            var days = Math.floor(elapse / (60 * 60 * 24)) % 30;
            var months = Math.floor(elapse / (60 * 60 * 24 * 30)) % 12;
            var years = Math.floor(elapse / (60 * 60 * 24 * 30 * 12));

            return start.getFullYear() + '年还剩' + years + '年' + months + '月' + days + '日'
                + hours + '小时' + minutes + '分' + seconds + '秒';
        }

        function domText(elem, text) {
            if (text == undefined) {

                if (elem.textContent) {
                    return elem.textContent;
                } else if (elem.innerText) {
                    return elem.innerText;
                }
            } else {
                if (elem.textContent) {
                    elem.textContent = text;
                } else if (elem.innerText) {
                    elem.innerText = text;
                } else {
                    elem.innerHTML = text;
                }
            }
        }

        var target = document.getElementById('target');

        setInterval(function () {
            domText(target, getTimeString());
        }, 1000)
    </script>

    </body>
    </html>


    完成一个函数,接受数组作为参数,数组元素为整数或者数组,数组元素包含整数或数组,函数返回扁平化后的数组

    如:[1, [2, [ [3, 4], 5], 6]] => [1, 2, 3, 4, 5, 6]
        var data =  [1, [2, [ [3, 4], 5], 6]];

        function flat(data, result) {
            var i, d, len;
            for (i = 0, len = data.length; i < len; ++i) {
                d = data[i];
                if (typeof d === 'number') {
                    result.push(d);
                } else {
                    flat(d, result);
                }
            }
        }

        var result = [];
        flat(data, result);

        console.log(result);


    如何判断一个对象是否为数组

    如果浏览器支持Array.isArray()可以直接判断否则需进行必要判断

    /**
    * 判断一个对象是否是数组,参数不是对象或者不是数组,返回false
    *
    * @param {Object} arg 需要测试是否为数组的对象
    * @return {Boolean} 传入参数是数组返回true,否则返回false
    */
    function isArray(arg) {
        if (typeof arg === 'object') {
            return Object.prototype.toString.call(arg) === '[object Array]';
        }
        return false;
    }


    请评价以下事件监听器代码并给出改进意见

    if (window.addEventListener) {
      var addListener = function (el, type, listener, useCapture) {
        el.addEventListener(type, listener, useCapture);
      };
    }
    else if (document.all) {
      addListener = function (el, type, listener) {
        el.attachEvent('on' + type, function () {
          listener.apply(el);
        });
      };
    }


    作用:浏览器功能检测实现跨浏览器DOM事件绑定

    优点:

    - 测试代码只运行一次,根据浏览器确定绑定方法
    - 通过listener.apply(el)解决IE下监听器this与标准不一致的地方
    - 在浏览器不支持的情况下提供简单的功能,在标准浏览器中提供捕获功能

    缺点:

    - document.all作为IE检测不可靠,应该使用if(el.attachEvent)
    - addListener在不同浏览器下API不一样
    - listener.apply使this与标准一致但监听器无法移除
    - 未解决IE下listener参数event。 target问题

    改进:

    var addListener;

    if (window.addEventListener) {
      addListener = function (el, type, listener, useCapture) {
        el.addEventListener(type, listener, useCapture);
        return listener;
      };
    }
    else if (window.attachEvent) {
      addListener = function (el, type, listener) {
        // 标准化this,event,target
        var wrapper = function () {
          var event = window.event;
          event.target = event.srcElement;
          listener.call(el, event);
        };

        el.attachEvent('on' + type, wrapper);
        return wrapper;
        // 返回wrapper。调用者可以保存,以后remove
      };
    }


    如何判断一个对象是否为函数

    /**
    * 判断对象是否为函数,如果当前运行环境对可调用对象(如正则表达式)
    * 的typeof返回'function',采用通用方法,否则采用优化方法
    *
    * @param {Any} arg 需要检测是否为函数的对象
    * @return {boolean} 如果参数是函数,返回true,否则false
    */
    function isFunction(arg) {
        if (arg) {
            if (typeof (/./) !== 'function') {
                return typeof arg === 'function';
            } else {
                return Object.prototype.toString.call(arg) === '[object Function]';
            }
        } // end if
        return false;
    }


    编写一个函数接受url中query string为参数,返回解析后的Object,query string使用application/x-www-form-urlencoded编码

    /**
    * 解析query string转换为对象,一个key有多个值时生成数组
    *
    * @param {String} query 需要解析的query字符串,开头可以是?,
    * 按照application/x-www-form-urlencoded编码
    * @return {Object} 参数解析后的对象
    */
    function parseQuery(query) {
        var result = {};

        // 如果不是字符串返回空对象
        if (typeof query !== 'string') {
            return result;
        }

        // 去掉字符串开头可能带的?
        if (query.charAt(0) === '?') {
            query = query.substring(1);
        }

        var pairs = query.split('&');
        var pair;
        var key, value;
        var i, len;

        for (i = 0, len = pairs.length; i < len; ++i) {
            pair = pairs[i].split('=');
            // application/x-www-form-urlencoded编码会将' '转换为+
            key = decodeURIComponent(pair[0]).replace(/+/g, ' ');
            value = decodeURIComponent(pair[1]).replace(/+/g, ' ');

            // 如果是新key,直接添加
            if (!(key in result)) {
                result[key] = value;
            }
            // 如果key已经出现一次以上,直接向数组添加value
            else if (isArray(result[key])) {
                result[key].push(value);
            }
            // key第二次出现,将结果改为数组
            else {
                var arr = [result[key]];
                arr.push(value);
                result[key] = arr;
            } // end if-else
        } // end for

        return result;
    }

    function isArray(arg) {
        if (arg && typeof arg === 'object') {
            return Object.prototype.toString.call(arg) === '[object Array]';
        }
        return false;
    }
    /**
    console.log(parseQuery('sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8'));
    */


    解析一个完整的url,返回Object包含域与window.location相同

    /**
    * 解析一个url并生成window.location对象中包含的域
    * location:
    * {
    *      href: '包含完整的url',
    *      origin: '包含协议到pathname之前的内容',
    *      protocol: 'url使用的协议,包含末尾的:',
    *      username: '用户名', // 暂时不支持
    *      password: '密码',  // 暂时不支持
    *      host: '完整主机名,包含:和端口',
    *      hostname: '主机名,不包含端口'
    *      port: '端口号',
    *      pathname: '服务器上访问资源的路径/开头',
    *      search: 'query string,?开头',
    *      hash: '#开头的fragment identifier'
    * }
    *
    * @param {string} url 需要解析的url
    * @return {Object} 包含url信息的对象
    */
    function parseUrl(url) {
        var result = {};
        var keys = ['href', 'origin', 'protocol', 'host',
                    'hostname', 'port', 'pathname', 'search', 'hash'];
        var i, len;
        var regexp = /(([^:]+:)//(([^:/?#]+)(:d+)?))(/[^?#]*)?(?[^#]*)?(#.*)?/;

        var match = regexp.exec(url);

        if (match) {
            for (i = keys.length - 1; i >= 0; --i) {
                result[keys[i]] = match[i] ? match[i] : '';
            }
        }

        return result;
    }


    完成函数getViewportSize返回指定窗口的视口尺寸

    /**
    * 查询指定窗口的视口尺寸,如果不指定窗口,查询当前窗口尺寸
    **/
    function getViewportSize(w) {
        w = w || window;

        // IE9及标准浏览器中可使用此标准方法
        if ('innerHeight' in w) {
            return {
                 w.innerWidth,
                height: w.innerHeight
            };
        }

        var d = w.document;
        // IE 8及以下浏览器在标准模式下
        if (document.compatMode === 'CSS1Compat') {
            return {
                 d.documentElement.clientWidth,
                height: d.documentElement.clientHeight
            };
        }

        // IE8及以下浏览器在怪癖模式下
        return {
             d.body.clientWidth,
            height: d.body.clientHeight
        };
    }


    完成函数getScrollOffset返回窗口滚动条偏移量

    /**
    * 获取指定window中滚动条的偏移量,如未指定则获取当前window
    * 滚动条偏移量
    *
    * @param {window} w 需要获取滚动条偏移量的窗口
    * @return {Object} obj.x为水平滚动条偏移量,obj.y为竖直滚动条偏移量
    */
    function getScrollOffset(w) {
        w =  w || window;
        // 如果是标准浏览器
        if (w.pageXOffset != null) {
            return {
                x: w.pageXOffset,
                y: w.pageYOffset
            };
        }

        // 老版本IE,根据兼容性不同访问不同元素
        var d = w.document;
        if (d.compatMode === 'CSS1Compat') {
            return {
                x: d.documentElement.scrollLeft,
                y: d.documentElement.scrollTop
            }
        }

        return {
            x: d.body.scrollLeft,
            y: d.body.scrollTop
        };
    }


    现有一个字符串richText,是一段富文本,需要显示在页面上.有个要求,需要给其中只包含一个img元素的p标签增加一个叫pic的class.请编写代码实现.可以使用jQuery或KISSY.

    function richText(text) {
        var div = document.createElement('div');
        div.innerHTML = text;
        var p = div.getElementsByTagName('p');
        var i, len;

        for (i = 0, len = p.length; i < len; ++i) {
            if (p[i].getElementsByTagName('img').length === 1) {
                p[i].classList.add('pic');
            }
        }

        return div.innerHTML;
    }


    请实现一个Event类,继承自此类的对象都会拥有两个方法on,off,once和trigger

    function Event() {
        if (!(this instanceof Event)) {
            return new Event();
        }
        this._callbacks = {};
    }
    Event.prototype.on = function (type, handler) {
        this_callbacks = this._callbacks || {};
        this._callbacks[type] = this.callbacks[type] || [];
        this._callbacks[type].push(handler);

        return this;
    };

    Event.prototype.off = function (type, handler) {
        var list = this._callbacks[type];

        if (list) {
            for (var i = list.length; i >= 0; --i) {
                if (list[i] === handler) {
                    list.splice(i, 1);
                }
            }
        }

        return this;
    };

    Event.prototype.trigger = function (type, data) {
        var list = this._callbacks[type];

        if (list) {
            for (var i = 0, len = list.length; i < len; ++i) {
                list[i].call(this, data);
            }
        }
    };

    Event.prototype.once = function (type, handler) {
        var self = this;

        function wrapper() {
            handler.apply(self, arguments);
            self.off(type, wrapper);
        }
        this.on(type, wrapper);
        return this;
    };


    编写一个函数将列表子元素顺序反转

    <ul id="target">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
    </ul>

    <script>
        var target = document.getElementById('target');
        var i;
        var frag = document.createDocumentFragment();

        for (i = target.children.length - 1; i &gt;= 0; --i) {
            frag.appendChild(target.children[i]);
        }
        target.appendChild(frag);
    </script>


    以下函数的作用是?空白区域应该填写什么

    // define
    (function (window) {
        function fn(str) {
            this.str = str;
        }

        fn.prototype.format = function () {
            var arg = __1__;
            return this.str.replace(__2__, function (a, b) {
                return arg[b] || '';
            });
        };

        window.fn = fn;
    })(window);

    // use
    (function () {
        var t = new fn('<p><a href="{0}">{1}</a><span>{2}</span></p>');
        console.log(t.format('http://www.alibaba.com', 'Alibaba', 'Welcome'));
    })();


    define部分定义一个简单的模板类,使用{}作为转义标记,中间的数字表示替换目标,format实参用来替换模板内标记 横线处填:

    - Array.prototype.slice.call(arguments, 0)
    - /{s*(d+)s*}/g

    编写一个函数实现form的序列化(即将一个表单中的键值序列化为可提交的字符串)

    <form id="target">
        <select name="age">
            <option value="aaa">aaa</option>
            <option value="bbb" selected>bbb</option>
        </select>
        <select name="friends" multiple>
            <option value="qiu" selected>qiu</option>
            <option value="de">de</option>
            <option value="qing" selected>qing</option>
        </select>
        <input name="name" value="qiudeqing">
        <input type="password" name="password" value="11111">
        <input type="hidden" name="salery" value="3333">
        <textarea name="description">description</textarea>
        <input type="checkbox" name="hobby" checked value="football">Football
        <input type="checkbox" name="hobby" value="basketball">Basketball
        <input type="radio" name="sex" checked value="Female">Female
        <input type="radio" name="sex" value="Male">Male
    </form>


    <script>

    /**
    * 将一个表单元素序列化为可提交的字符串
    *
    * @param {FormElement} form 需要序列化的表单元素
    * @return {string} 表单序列化后的字符串
    */
    function serializeForm(form) {
      if (!form || form.nodeName.toUpperCase() !== 'FORM') {
        return;
      }

      var result = [];

      var i, len;
      var field, fieldName, fieldType;

      for (i = 0, len = form.length; i < len; ++i) {
        field = form.elements[i];
        fieldName = field.name;
        fieldType = field.type;

        if (field.disabled || !fieldName) {
          continue;
        } // enf if

        switch (fieldType) {
          case 'text':
          case 'password':
          case 'hidden':
          case 'textarea':
            result.push(encodeURIComponent(fieldName) + '=' +
                encodeURIComponent(field.value));
            break;

          case 'radio':
          case 'checkbox':
            if (field.checked) {
              result.push(encodeURIComponent(fieldName) + '=' +
                encodeURIComponent(field.value));
            }
            break;

          case 'select-one':
          case 'select-multiple':
            for (var j = 0, jLen = field.options.length; j < jLen; ++j) {
              if (field.options[j].selected) {
                result.push(encodeURIComponent(fieldName) + '=' +
                  encodeURIComponent(field.options[j].value || field.options[j].text));
              }
            } // end for
            break;

          case 'file':
          case 'submit':
            break; // 是否处理?

          default:
            break;
        } // end switch
      } // end for

        return result.join('&');
    }

    var form = document.getElementById('target');
    console.log(serializeForm(form));
    </script>


    使用原生javascript给下面列表中的li节点绑定点击事件,点击时创建一个Object对象,兼容IE和标准浏览器

    <ul id="nav">
        <li><a href="http://11111">111</a></li>
        <li><a href="http://2222">222</a></li>
        <li><a href="http://333">333</a></li>
        <li><a href="http://444">444</a></li>
    </ul>

    Object:
    {
        "index": 1,
        "name": "111",
        "link": "http://1111"
    }


    script:

    var EventUtil = {
        getEvent: function (event) {
            return event || window.event;
        },
        getTarget: function (event) {
            return event.target || event.srcElement;
        },
        // 返回注册成功的监听器,IE中需要使用返回值来移除监听器
        on: function (elem, type, handler) {
            if (elem.addEventListener) {
                elem.addEventListener(type, handler, false);
                return handler;
            } else if (elem.attachEvent) {
                function wrapper(event) {
                    return handler.call(elem, event);
                };
                elem.attachEvent('on' + type, wrapper);
                return wrapper;
            }
        },
        off: function (elem, type, handler) {
            if (elem.removeEventListener) {
                elem.removeEventListener(type, handler, false);
            } else if (elem.detachEvent) {
                elem.detachEvent('on' + type, handler);
            }
        },
        preventDefault: function (event) {
            if (event.preventDefault) {
                event.preventDefault();
            } else if ('returnValue' in event) {
                event.returnValue = false;
            }
        },
        stopPropagation: function (event) {
            if (event.stopPropagation) {
                event.stopPropagation();
            } else if ('cancelBubble' in event) {
                event.cancelBubble = true;
            }
        }
    };
    var DOMUtil = {
        text: function (elem) {
            if ('textContent' in elem) {
                return elem.textContent;
            } else if ('innerText' in elem) {
                return elem.innerText;
            }
        },
        prop: function (elem, propName) {
            return elem.getAttribute(propName);
        }
    };

    var nav = document.getElementById('nav');

    EventUtil.on(nav, 'click', function (event) {
        var event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);

        var children = this.children;
        var i, len;
        var anchor;
        var obj = {};

        for (i = 0, len = children.length; i < len; ++i) {
            if (children[i] === target) {
                obj.index = i + 1;
                anchor = target.getElementsByTagName('a')[0];
                obj.name = DOMUtil.text(anchor);
                obj.link = DOMUtil.prop(anchor, 'href');
            }
        }

        alert('index: ' + obj.index + ' name: ' + obj.name +
            ' link: ' + obj.link);
    });


    有一个大数组,var a = ['1', '2', '3', ...];a的长度是100,内容填充随机整数的字符串.请先构造此数组a,然后设计一个算法将其内容去重

        /**
        * 数组去重
        **/
        function normalize(arr) {
            if (arr && Array.isArray(arr)) {
                var i, len, map = {};
                for (i = arr.length; i >= 0; --i) {
                    if (arr[i] in map) {
                        arr.splice(i, 1);
                    } else {
                        map[arr[i]] = true;
                    }
                }
            }
            return arr;
        }

        /**
        * 用100个随机整数对应的字符串填充数组。
        **/
        function fillArray(arr, start, end) {
            start = start == undefined ? 1 : start;
            end = end == undefined ?  100 : end;

            if (end <= start) {
                end = start + 100;
            }

            var width = end - start;
            var i;
            for (i = 100; i >= 1; --i) {
                arr.push('' + (Math.floor(Math.random() * width) + start));
            }
            return arr;
        }

        var input = [];
        fillArray(input, 1, 100);
        input.sort(function (a, b) {
            return a - b;
        });
        console.log(input);

        normalize(input);
        console.log(input);
    面试有几点需注意

        面试题目: 根据你的等级和职位变化,入门级到专家级:范围↑、深度↑、方向↑。

        题目类型: 技术视野、项目细节、理论知识题,算法题,开放性题,案例题。

        进行追问: 可以确保问到你开始不懂或面试官开始不懂为止,这样可以大大延展题目的区分度和深度,知道你的实际能力。因为这种关联知识是长时期的学习,绝对不是临时记得住的。

        回答问题再棒,面试官(可能是你的直接领导面试),会考虑我要不要这个人做我的同事?所以态度很重要。(感觉更像是相亲)

        资深的工程师能把absolute和relative弄混,这样的人不要也罢,因为团队需要的你这个人具有可以依靠的才能(靠谱)。

    前端开发面试知识点大纲:

    HTML&CSS:
        对Web标准的理解、浏览器内核差异、兼容性、hack、CSS基本功:布局、盒子模型、选择器优先级及使用、HTML5、CSS3、移动端适应

    JavaScript:
        数据类型、面向对象、继承、闭包、插件、作用域、跨域、原型链、模块化、自定义事件、内存泄漏、事件机制、异步装载回调、模板引擎、Nodejs、JSON、ajax等。

    其他:
       HTTP、安全、正则、优化、重构、响应式、移动端、团队协作、可维护、SEO、UED、架构、职业生涯

    作为一名前端工程师,无论工作年头长短都应该必须掌握的知识点:

        1、DOM结构 —— 两个节点之间可能存在哪些关系以及如何在节点之间任意移动。

        2、DOM操作  ——如何添加、移除、移动、复制、创建和查找节点等。

        3、事件    —— 如何使用事件,以及IE和标准DOM事件模型之间存在的差别。

        4、XMLHttpRequest —— 这是什么、怎样完整地执行一次GET请求、怎样检测错误。

        5、严格模式与混杂模式 —— 如何触发这两种模式,区分它们有何意义。

        6、盒模型 —— 外边距、内边距和边框之间的关系,及IE8以下版本的浏览器中的盒模型

        7、块级元素与行内元素 —— 怎么用CSS控制它们、以及如何合理的使用它们

        8、浮动元素——怎么使用它们、它们有什么问题以及怎么解决这些问题。

        9、HTML与XHTML——二者有什么区别,你觉得应该使用哪一个并说出理由。

        10、JSON  —— 作用、用途、设计结构。

    HTML

        Doctype作用? 严格模式与混杂模式如何区分?它们有何意义?

        (1)、<!DOCTYPE> 声明位于文档中的最前面,处于 <html> 标签之前。告知浏览器的解析器,
              用什么文档类型 规范来解析这个文档。

        (2)、严格模式的排版和 JS 运作模式是  以该浏览器支持的最高标准运行。

        (3)、在混杂模式中,页面以宽松的向后兼容的方式显示。模拟老式浏览器的行为以防止站点无法工作。

        (4)、DOCTYPE不存在或格式不正确会导致文档以混杂模式呈现。

        行内元素有哪些?块级元素有哪些? 空(void)元素有那些?

        (1)CSS规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,
          比如div默认display属性值为“block”,成为“块级”元素;
          span默认display属性值为“inline”,是“行内”元素。

        (2)行内元素有:a b span img input select strong(强调的语气)
         块级元素有:div ul ol li dl dt dd h1 h2 h3 h4…p

        (3)知名的空元素:
        <br> <hr> <img> <input> <link> <meta>
        鲜为人知的是:
        <area> <base> <col> <command> <embed> <keygen> <param> <source> <track> <wbr>

        link 和@import 的区别是?

        (1)link属于XHTML标签,而@import是CSS提供的;

        (2)页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载;

        (3)import只在IE5以上才能识别,而link是XHTML标签,无兼容问题;

        (4)link方式的样式的权重 高于@import的权重.

        浏览器的内核分别是什么?

         * IE浏览器的内核Trident、Mozilla的Gecko、Chrome的Blink(WebKit的分支)、Opera内核原为Presto,现为Blink;

        常见兼容性问题?

        * png24位的图片在iE6浏览器上出现背景,解决方案是做成PNG8.

        * 浏览器默认的margin和padding不同。解决方案是加一个全局的*{margin:0;padding:0;}来统一。

        * IE6双边距bug:块属性标签float后,又有横行的margin情况下,在ie6显示margin比设置的大。

          浮动ie产生的双倍距离 #box{ float:left; 10px; margin:0 0 0 10px;}

         这种情况之下IE会产生20px的距离,解决方案是在float的标签样式控制中加入 ——_display:inline;将其转化为行内属性。(_这个符号只有ie6会识别)

          渐进识别的方式,从总体中逐渐排除局部。

          首先,巧妙的使用“9”这一标记,将IE游览器从所有情况中分离出来。
          接着,再次使用“+”将IE8和IE7、IE6分离开来,这样IE8已经独立识别。

          css
              .bb{
               
              .
              +
              _
              }

        *  IE下,可以使用获取常规属性的方法来获取自定义属性,
           也可以使用getAttribute()获取自定义属性;
           Firefox下,只能使用getAttribute()获取自定义属性.
           解决方法:统一通过getAttribute()获取自定义属性.

        * IE下,even对象有x,y属性,但是没有pageX,pageY属性;
          Firefox下,event对象有pageX,pageY属性,但是没有x,y属性.

        * 解决方法:(条件注释)缺点是在IE浏览器下可能会增加额外的HTTP请求数。

        * Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,
          可通过加入 CSS 属性 -webkit-text-size-adjust: none; 解决.

        超链接访问过后hover样式就不出现了 被点击访问过的超链接样式不在具有hover和active了解决方法是改变CSS属性的排列顺序:
        L-V-H-A :  a:link {} a:visited {} a:hover {} a:active {}

        html5有哪些新特性、移除了那些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分 HTML 和 HTML5?

        * HTML5 现在已经不是 SGML 的子集,主要是关于图像,位置,存储,多任务等功能的增加。

        * 绘画 canvas
          用于媒介回放的 video 和 audio 元素
          本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
          sessionStorage 的数据在浏览器关闭后自动删除

          语意化更好的内容元素,比如 article、footer、header、nav、section
          表单控件,calendar、date、time、email、url、search
          新的技术webworker, websockt, Geolocation

        * 移除的元素

        纯表现的元素:basefont,big,center,font, s,strike,tt,u;

        对可用性产生负面影响的元素:frame,frameset,noframes;

        支持HTML5新标签:

        * IE8/IE7/IE6支持通过document.createElement方法产生的标签,
          可以利用这一特性让这些浏览器支持HTML5新标签,

          浏览器支持新标签后,还需要添加标签默认的样式:

        * 当然最好的方式是直接使用成熟的框架、使用最多的是html5shim框架
           <!--[if lt IE 9]>
           <script> src="http://html5shim.googlecode.com/svn/trunk/html5.js"</script>
           <![endif]-->
        如何区分: DOCTYPE声明新增的结构元素功能元素

        语义化的理解?

        用正确的标签做正确的事情!
        html语义化就是让页面的内容结构化,便于对浏览器、搜索引擎解析;
        在没有样式CCS情况下也以一种文档格式显示,并且是容易阅读的。
        搜索引擎的爬虫依赖于标记来确定上下文和各个关键字的权重,利于 SEO。
        使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。

        HTML5的离线储存?

        localStorage    长期存储数据,浏览器关闭后数据不丢失;
        sessionStorage  数据在浏览器关闭后自动删除。

        (写)描述一段语义的html代码吧。

        (HTML5中新增加的很多标签(如:<article>、<nav>、<header>和<footer>等)
         就是基于语义化设计原则)
            < div id="header">
            < h1>标题< /h1>
            < h2>专注Web前端技术< /h2>
            < /div>

        iframe有那些缺点?

        *iframe会阻塞主页面的Onload事件;

        *iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。
        使用iframe之前需要考虑这两个缺点。如果需要使用iframe,最好是通过javascript
        动态给iframe添加src属性值,这样可以可以绕开以上两个问题。

        请描述一下 cookies,sessionStorage 和 localStorage 的区别?

        cookie在浏览器和服务器间来回传递。 sessionStorage和localStorage不会
        sessionStorage和localStorage的存储空间更大;
        sessionStorage和localStorage有更多丰富易用的接口;
        sessionStorage和localStorage各自独立的存储空间;

    CSS

        介绍一下CSS的盒子模型?

        (1)有两种, IE 盒子模型、标准 W3C 盒子模型;IE的content部分包含了 border 和 pading;

        (2)盒模型: 内容(content)、填充(padding)、边界(margin)、 边框(border).

        CSS 选择符有哪些?哪些属性可以继承?优先级算法如何计算? CSS3新增伪类有那些?

        *   1.id选择器( # myid)
            2.类选择器(.myclassname)
            3.标签选择器(div, h1, p)
            4.相邻选择器(h1 + p)
            5.子选择器(ul < li)
            6.后代选择器(li a)
            7.通配符选择器( * )
            8.属性选择器(a[rel = "external"])
            9.伪类选择器(a: hover, li: nth - child)

        *   可继承的样式: font-size font-family color, UL LI DL DD DT;

        *   不可继承的样式:border padding margin width height ;

        *   优先级就近原则,同权重情况下样式定义最近者为准;

        *   载入样式以最后载入的定位为准;

        优先级为:

           !important >  id > class > tag

           important 比 内联优先级高

        CSS3新增伪类举例:

        p:first-of-type 选择属于其父元素的首个 <p> 元素的每个 <p> 元素。
        p:last-of-type  选择属于其父元素的最后 <p> 元素的每个 <p> 元素。
        p:only-of-type  选择属于其父元素唯一的 <p> 元素的每个 <p> 元素。
        p:only-child    选择属于其父元素的唯一子元素的每个 <p> 元素。
        p:nth-child(2)  选择属于其父元素的第二个子元素的每个 <p> 元素。
        :enabled  :disabled 控制表单控件的禁用状态。
        :checked        单选框或复选框被选中。

        如何居中div?如何居中一个浮动元素?

            给div设置一个宽度,然后添加margin:0 auto属性

            div{
                200px;
                margin:0 auto;
             }

            居中一个浮动元素

              确定容器的宽高 宽500 高 300 的层
              设置层的外边距

             .div {
              Width:500px ; height:300px;//高度可以不设
              Margin: -150px 0 0 -250px;
              position:relative;相对定位
              //方便看效果
              left:50%;
              top:50%;
            }

        列出display的值,说明他们的作用。position的值, relative和absolute定位原点是?

          1.
          block 象块类型元素一样显示。
          none 缺省值。象行内元素类型一样显示。
          inline-block 象行内元素一样显示,但其内容象块类型元素一样显示。
          list-item 象块类型元素一样显示,并添加样式列表标记。

          2.
          *absolute
                生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。

          *fixed (老IE不支持)
                生成绝对定位的元素,相对于浏览器窗口进行定位。

          *relative
                生成相对定位的元素,相对于其正常位置进行定位。

          * static  默认值。没有定位,元素出现在正常的流中
          *(忽略 top, bottom, left, right z-index 声明)。

          * inherit 规定从父元素继承 position 属性的值。

        CSS3有哪些新特性?

          CSS3实现圆角(border-radius:8px),阴影(box-shadow:10px),
          对文字加特效(text-shadow、),线性渐变(gradient),旋转(transform)
          transform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);//旋转,缩放,定位,倾斜
          增加了更多的CSS选择器  多背景 rgba

        一个满屏 品 字布局 如何设计?

        经常遇到的CSS的兼容性有哪些?原因,解决方法是什么?

        为什么要初始化CSS样式。

        - 因为浏览器的兼容问题,不同浏览器对有些标签的默认值是不同的,如果没对CSS初始化往往会出现浏览器之间的页面显示差异。

        - 当然,初始化样式会对SEO有一定的影响,但鱼和熊掌不可兼得,但力求影响最小的情况下初始化。

        *最简单的初始化方法就是: * {padding: 0; margin: 0;} (不建议)

        淘宝的样式初始化:
        body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0; }
        body, button, input, select, textarea { font:12px/1.5tahoma, arial, 5b8b4f53; }
        h1, h2, h3, h4, h5, h6{ font-size:100%; }
        address, cite, dfn, em, var { font-style:normal; }
        code, kbd, pre, samp { font-family:couriernew, courier, monospace; }
        small{ font-size:12px; }
        ul, ol { list-style:none; }
        a { text-decoration:none; }
        a:hover { text-decoration:underline; }
        sup { vertical-align:text-top; }
        sub{ vertical-align:text-bottom; }
        legend { color:#000; }
        fieldset, img { border:0; }
        button, input, select, textarea { font-size:100%; }
        table { border-collapse:collapse; border-spacing:0; }

        absolute的containing block计算方式跟正常流有什么不同?

        position跟display、margin collapse、overflow、float这些特性相互叠加后会怎么样?

        对BFC规范的理解?

        (W3C CSS 2.1 规范中的一个概念,它决定了元素如何对其内容进行定位,以及与其他元素的关 系和相互作用。)

        css定义的权重

        以下是权重的规则:标签的权重为1,class的权重为10,id的权重为100,以下例子是演示各种定义的权重值:


        div{
        }

        .class1{
        }

        #id1{
        }

        #id1 div{
        }

        .class1 div{
        }

        .class1 .class2 div{
        }

        如果权重相同,则最后定义的样式会起作用,但是应该避免这种情况出现

        解释下浮动和它的工作原理?清除浮动的技巧

        用过媒体查询,针对移动端的布局吗?

        使用 CSS 预处理器吗?喜欢那个?

        SASS

    JavaScript

        JavaScript原型,原型链 ? 有什么特点?
        在JavaScript中,一共有两种类型的值,原始值和对象值.每个对象都有一个内部属性[[prototype]],我们通常称之为原型.原型的值可以是一个对象,也可以是null.如果它的值是一个对象,则这个对象也一定有自己的原型.这  样就形成了一条线性的链,我们称之为原型链.
    访问一个对象的原型可以使用ES5中的Object.getPrototypeOf方法,或者ES6中的__proto__属性.
    原型链的作用是用来实现继承,比如我们新建一个数组,数组的方法就是从数组的原型上继承而来的.
    var arr = [];
    arr.map === Array.prototype.map //arr.map是从arr.__proto__上继承下来的,arr.__proto__也就是Array.prototype

        eval是做什么的?

        它的功能是把对应的字符串解析成JS代码并运行;
        应该避免使用eval,不安全,非常耗性能(2次,一次解析成js语句,一次执行)。

        null,undefined 的区别?
       Undefined类型只有一个值,即undefined。当声明的变量还未被初始化时,变量的默认值为undefined。
       Null类型也只有一个值,即null。null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。
        写一个通用的事件侦听器函数。

            // event(事件)工具集,来源:github.com/markyun
            markyun.Event = {
                // 页面加载完成后
                readyEvent : function(fn) {
                    if (fn==null) {
                        fn=document;
                    }
                    var oldonload = window.onload;
                    if (typeof window.onload != 'function') {
                        window.onload = fn;
                    } else {
                        window.onload = function() {
                            oldonload();
                            fn();
                        };
                    }
                },
                // 视能力分别使用dom0||dom2||IE方式 来绑定事件
                // 参数: 操作的元素,事件名称 ,事件处理程序
                addEvent : function(element, type, handler) {
                    if (element.addEventListener) {
                        //事件类型、需要执行的函数、是否捕捉
                        element.addEventListener(type, handler, false);
                    } else if (element.attachEvent) {
                        element.attachEvent('on' + type, function() {
                            handler.call(element);
                        });
                    } else {
                        element['on' + type] = handler;
                    }
                },
                // 移除事件
                removeEvent : function(element, type, handler) {
                    if (element.removeEnentListener) {
                        element.removeEnentListener(type, handler, false);
                    } else if (element.datachEvent) {
                        element.detachEvent('on' + type, handler);
                    } else {
                        element['on' + type] = null;
                    }
                },
                // 阻止事件 (主要是事件冒泡,因为IE不支持事件捕获)
                stopPropagation : function(ev) {
                    if (ev.stopPropagation) {
                        ev.stopPropagation();
                    } else {
                        ev.cancelBubble = true;
                    }
                },
                // 取消事件的默认行为
                preventDefault : function(event) {
                    if (event.preventDefault) {
                        event.preventDefault();
                    } else {
                        event.returnValue = false;
                    }
                },
                // 获取事件目标
                getTarget : function(event) {
                    return event.target || event.srcElement;
                },
                // 获取event对象的引用,取到事件的所有信息,确保随时能使用event;
                getEvent : function(e) {
                    var ev = e || window.event;
                    if (!ev) {
                        var c = this.getEvent.caller;
                        while (c) {
                            ev = c.arguments[0];
                            if (ev && Event == ev.constructor) {
                                break;
                            }
                            c = c.caller;
                        }
                    }
                    return ev;
                }
            };

        Node.js的适用场景?

        高并发、聊天、实时消息推送

        介绍js的基本数据类型。

        number,string,boolean,object,undefined

        Javascript如何实现继承?

        通过原型和构造器

        ["1", "2", "3"].map(parseInt) 答案是多少?

         [1, NaN, NaN] 因为 parseInt 需要两个参数 (val, radix) 但 map 传了 3 个 (element, index, array)

        如何创建一个对象? (画出此对象的内存图)

          function Person(name, age) {
            this.name = name;
            this.age = age;
            this.sing = function() { alert(this.name) }
          }

        谈谈This对象的理解。

        this是js的一个关键字,随着函数使用场合不同,this的值会发生变化。

        但是有一个总原则,那就是this指的是调用函数的那个对象。

        this一般情况下:是全局对象Global。 作为方法调用,那么this就是指这个对象

        事件是?IE与火狐的事件机制有什么区别? 如何阻止冒泡?

         1. 我们在网页中的某个操作(有的操作对应多个事件)。例如:当我们点击一个按钮就会产生一个事件。是可以被 JavaScript 侦测到的行为。
         2. 事件处理机制:IE是事件冒泡、火狐是 事件捕获;
         3. ev.stopPropagation();

        什么是闭包(closure),为什么要用它?

        执行say667()后,say667()闭包内部变量会存在,而闭包内部函数的内部变量不会存在.使得Javascript的垃圾回收机制GC不会收回say667()所占用的资源,因为say667()的内部函数的执行需要依赖say667()中的变量。这是对闭包作用的非常直白的描述.

          function say667() {
            // Local variable that ends up within closure
            var num = 666;
            var sayAlert = function() { alert(num); }
            num++;
            return sayAlert;
        }

         var sayAlert = say667();
         sayAlert()//执行结果应该弹出的667

        "use strict";是什么意思 ? 使用它的好处和坏处分别是什么?

        如何判断一个对象是否属于某个类?

          使用instanceof (待完善)

           if(a instanceof Person){
               alert('yes');
           }

        new操作符具体干了什么呢?

             1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
             2、属性和方法被加入到 this 引用的对象中。
             3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。

        var obj  = {};
        obj.__proto__ = Base.prototype;
        Base.call(obj);

        Javascript中,有一个函数,执行时对象查找时,永远不会去查找原型,这个函数是?

        hasOwnProperty

        JSON 的了解?

        JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。
        它是基于JavaScript的一个子集。数据格式简单, 易于读写, 占用带宽小
        {'age':'12', 'name':'back'}

        js延迟加载的方式有哪些?

        defer和async、动态创建DOM方式(用得最多)、按需异步载入js

        ajax 是什么?
    ajax的全称是AsynchronousJavascript+XML。异步传输+js+xml。所谓异步,在这里简单地解释就是:向服务器发送请求的时候,我们不必等待结果,而是可以同时做其他的事情,等到有了结果我们可以再来处理这个事。(当然,在其他语境下这个解释可能就不对了)这个很重要,如果不是这样的话,我们点完按钮,页面就会死在那里,其他的数据请求不会往下走了。这样比等待刷新似乎更加讨厌。(虽然提供异步通讯功能的组件默认情况下都是异步的,但它们也提供了同步选项,如果你好奇把那个选项改为false的话,你的页面就会死在那里)xml只是一种数据格式,在这件事里并不重要,我们在更新一行字的时候理论上说不需要这个格式,但如果我们更新很多内容,那么格式化的数据可以使我们有条理地去实现更新。现在大部分人其实是用JSON这种格式来代替XML的,因为前者更加简洁,据说目前的解析速度也更快。多快好省,能省则省啊。

        同步和异步的区别?
    javascript 异步表示async,指:代码执行不按顺序,‘跳过’执行,待其他某些代码执行完后,再来执行,称为“异步”。
    javascript同步表示sync,指:代码依次执行。
    http://blog.sina.com.cn/s/blog_6e7ba6d10101dncr.html
        如何解决跨域问题?

        jsonp、 iframe、window.name、window.postMessage、服务器上设置代理页面

        模块化怎么做?

        立即执行函数,不暴露私有成员

            var module1 = (function(){
                var _count = 0;
                var m1 = function(){
                  //...
                };
                var m2 = function(){
                  //...
                };
                return {
                  m1 : m1,
                  m2 : m2
                };
              })();

        AMD(Modules/Asynchronous-Definition)、CMD(Common Module Definition)规范区别?

        异步加载的方式有哪些?

          (1) defer,只支持IE

          (2) async:

          (3) 创建script,插入到DOM中,加载完毕后callBack

        documen.write和 innerHTML的区别

    document.write只能重绘整个页面

    innerHTML可以重绘页面的一部分

        .call() 和 .apply() 的区别?

          例子中用 add 来替换 sub,add.call(sub,3,1) == add(3,1) ,所以运行结果为:alert(4);

          注意:js 中的函数其实是对象,函数名是对 Function 对象的引用。

            function add(a,b)
            {
                alert(a+b);
            }

            function sub(a,b)
            {
                alert(a-b);
            }

            add.call(sub,3,1);

        Jquery与jQuery UI 有啥区别?

        *jQuery是一个js库,主要提供的功能是选择器,属性修改和事件绑定等等。

        *jQuery UI则是在jQuery的基础上,利用jQuery的扩展性,设计的插件。
         提供了一些常用的界面元素,诸如对话框、拖动行为、改变大小行为等等

        JQuery的源码看过吗?能不能简单说一下它的实现原理?
    就是封装起来的JS代码。
        jquery 中如何将数组转化为json字符串,然后再转化回来?
    JSON.stringify 把一个对象转换成json字符串,
    JSON.parse 把一个json字符串解析成对象。

    jQuery中没有提供这个功能,所以你需要先编写两个jQuery的扩展:

        $.fn.stringifyArray = function(array) {
            return JSON.stringify(array)
        }

        $.fn.parseArray = function(array) {
            return JSON.parse(array)
        }

        然后调用:
        $("").stringifyArray(array)

        针对 jQuery 的优化方法?

        *基于Class的选择性的性能相对于Id选择器开销很大,因为需遍历所有DOM元素。

        *频繁操作的DOM,先缓存起来再操作。用Jquery的链式调用更好。
         比如:var str=$("a").attr("href");

        *for (var i = size; i < arr.length; i++) {}
         for 循环每一次循环都查找了数组 (arr) 的.length 属性,在开始循环的时候设置一个变量来存储这个数字,可以让循环跑得更快:
         for (var i = size, length = arr.length; i < length; i++) {}

        JavaScript中的作用域与变量声明提升?
    http://blog.csdn.net/sunxing007/article/details/9034253

        如何编写高性能的Javascript?
    http://developer.51cto.com/art/200906/131335.htm
        那些操作会造成内存泄漏?
    http://www.cnblogs.com/carekee/articles/1733847.html
        内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。
        垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。

        setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
        闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)

        JQuery一个对象可以同时绑定多个事件,这是如何实现的?
    利用选择器的方法,已百度找到。如:$("#div1", "#divN").click(function() { });
    其他问题

        你遇到过比较难的技术问题是?你是如何解决的?

        常使用的库有哪些?常用的前端开发工具?开发过什么应用或组件?

        页面重构怎么操作?

        列举IE 与其他浏览器不一样的特性?

        99%的网站都需要被重构是那本书上写的?

        什么叫优雅降级和渐进增强?

        WEB应用从服务器主动推送Data到客户端有那些方式?

        对Node的优点和缺点提出了自己的看法?

        *(优点)因为Node是基于事件驱动和无阻塞的,所以非常适合处理并发请求,
          因此构建在Node上的代理服务器相比其他技术实现(如Ruby)的服务器表现要好得多。
          此外,与Node代理服务器交互的客户端代码是由javascript语言编写的,
          因此客户端和服务器端都用同一种语言编写,这是非常美妙的事情。

        *(缺点)Node是一个相对新的开源项目,所以不太稳定,它总是一直在变,
          而且缺少足够多的第三方库支持。看起来,就像是Ruby/Rails当年的样子。

        你有哪些性能优化的方法?

         (看雅虎14条性能优化原则)。

          (1) 减少http请求次数:CSS Sprites, JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存 ,图片服务器。

          (2) 前端模板 JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数

          (3) 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。

          (4) 当需要设置的样式很多时设置className而不是直接操作style。

          (5) 少用全局变量、缓存DOM节点查找的结果。减少IO读取操作。

          (6) 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。

          (7) 图片预加载,将样式表放在顶部,将脚本放在底部  加上时间戳。

          (8) 避免在页面的主体布局中使用table,table要等其中的内容完全下载之后才会显示出来,显示比div+css布局慢。

        http状态码有那些?分别代表是什么意思?

        100-199 用于指定客户端应相应的某些动作。
        200-299 用于表示请求成功。
        300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。
        400-499 用于指出客户端的错误。400    1、语义有误,当前请求无法被服务器理解。401   当前请求需要用户验证 403  服务器已经理解请求,但是拒绝执行它。
        500-599 用于支持服务器错误。 503 – 服务不可用

        一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?(流程说的越详细越好)

            查找浏览器缓存
            DNS解析、查找该域名对应的IP地址、重定向(301)、发出第二个GET请求
            进行HTTP协议会话
            客户端发送报头(请求报头)
            服务器回馈报头(响应报头)
            html文档开始下载
            文档树建立,根据标记请求所需指定MIME类型的文件
            文件显示
            [
            浏览器这边做的工作大致分为以下几步:

            加载:根据请求的URL进行域名解析,向服务器发起请求,接收文件(HTML、JS、CSS、图象等)。

            解析:对加载到的资源(HTML、JS、CSS等)进行语法解析,建议相应的内部数据结构(比如HTML的DOM树,JS的(对象)属性表,CSS的样式规则等等)
            }

        除了前端以外还了解什么其它技术么?你最最厉害的技能是什么?

        你常用的开发工具是什么,为什么?

        对前端界面工程师这个职位是怎么样理解的?它的前景会怎么样?

             前端是最贴近用户的程序员,比后端、数据库、产品经理、运营、安全都近。
            1、实现界面交互
            2、提升用户体验
            3、有了Node.js,前端可以实现服务端的一些事情

        前端是最贴近用户的程序员,前端的能力就是能让产品从 90分进化到 100 分,甚至更好,

         参与项目,快速高质量完成实现效果图,精确到1px;

         与团队成员,UI设计,产品经理的沟通;

         做好的页面结构,页面重构和用户体验;

         处理hack,兼容、写出优美的代码格式;

         针对服务器的优化、拥抱最新前端技术。

  • 相关阅读:
    HTML5新增标签和属性
    HTML——表单标签
    HTML——表格标签
    js
    js
    js
    js
    js-02-2
    js
    selleck --手机端-- 销售打卡记录下载
  • 原文地址:https://www.cnblogs.com/new-Spring/p/5861841.html
Copyright © 2020-2023  润新知