谈笑间学会前端

459 阅读31分钟

由来:记录有关前端的知识点!!!


1. for、forEach 、for in、for of 循环的区别

    1. for:是js中原始的写法它用来遍历数组
var arr = [1,2,3]
for(var i = 0 ; i< arr.length ; i++){
    console.log(arr[i]) // 1,2,3
}
    1. forEach: 是ES5中的语法,用来遍历数组,但是它不支持使用break、continue和return语句
let arr = ['a', 'b', 'c']
arr.forEach(function (value, index, arr) { // value是当前元素,index当前元素索引,arr数组
    console.log(value + ', index = ' + index) 
})
output:
a, index = 0
b, index = 1
c, index = 2
    1. for in: 遍历数组索引、对象的属性(循环不仅会遍历数组元素,还会遍历任意其他自定义添加的属性)
let obj = {name: 'LiMing', age: '18'}
obj.phone="13513513512"
for (let o in obj) {
    console.log(o,obj[o])
}
output:
name LiMing
age 18
phone 13513513512
    1. for of: 是ES6中新增加的语法,遍历数组,不支持遍历对象,支持return
let arr = ['A', 'B', 'C']
for (let o of arr) {
    console.log(o) //A, B, C
}

解决for of遍历对象:
let obj = {name: 'LiMing', age: '18'}
for (let o of Object.keys(obj)) {
    console.log(o,obj[o])
}
output:
name LiMing
age 18

2. this的指向形式有哪些? 能改变this指向的方法有哪些?(如何确定this:看谁调用的,前面是谁,this就是谁!!!)

1)this的指向形式有:
    a.普通函数调用,此时 this 指向window
    b.在严格模式下"use strict",为undefined;
    c.构造函数调用, 此时 this 指向实例对象
    d.对象方法调用, 此时 this 指向该方法所属的对象
    e.通过事件绑定的方法, 此时 this 指向绑定事件的对象
    f.定时器函数, 此时 this 指向window
    
2)能改变this指向的方法:
     a.call
     b.apply
     c.bind
     d.var that=this;
     e.箭头函数 =>
扩展:

Function.apply(obj,args)方法能接收两个参数
    obj:这个对象将代替Function类里this对象
    args:这个是数组,它将作为参数传给Function(args-->arguments)

Function.call(obj,[param1[,param2[,…[,paramN]]]])
    obj:这个对象将代替Function类里this对象
    params:这个是一个参数列表
    
什么情况下用apply,什么情况下用call?

在给对象参数的情况下,如果参数的形式是数组的时候,参数的列表是对应一致的 就可以采用 apply ;

如果Person的参数列表是这样的(age,name),而Student的参数列表是(name,age,grade),这样就可以用call来实现了,也就是直接指定参数列表对应值的位置(Person.call(this,age,name,grade));

3. JS中如何阻止事件冒泡,事件捕获和事件默认行为

1)事件冒泡:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发;
if(e.stopPropagation()){
    e.stopPropagation();
}else{
    e.cancelBubble = true; //兼容IE方法
}
2)事件捕获:事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定);
 if(ele.addEventListener){
    ele.addEventListener("click", function (){...});
 }else{
    ele.attachEvent("onclick",function (){...}); // 兼容IE方法
 }
3)事件默认行为:即浏览器本身自带的功能;
 if (e.preventDefault) {
    e.preventDefault();
} else { 
    e.returnValue = false; // 兼容IE方法
};      

IE事件模型相比w3c事件模型的区别:

1.绑定事件的函数不一样,IE中用attachEvent(),移除事件用detachEvent();
2.绑定事件必须加‘on’;
3.IE7,8中先绑定的事件后发生,也存在随机发生的可能;
4.绑定的事件中,this的指向不同;w3c模型中,this指向对象本身;而IE模型中ths指向window;


4. 谈谈类型比较

1)如果两个值类型不相同,则它们不相等;
var a1="val1";
var a2="val2";
console.log(a1==a2);//false

2)如果两个值都是null或者都是undefined,则它们相等;
var a1=null;
var a2=null;
console.log(a1===a2);//true

var a3=undefined;
var a4=undefined;
console.log(a3===a4);//true

3)如果两个值都是布尔值true或者都是布尔值false,则它们相等;
var a1=true;
var a2=true;
console.log(a1===a2);//true

4)如果其中一个值是NaN,或者两个值都是NaN,则它们不相等。NaN和其他任何值都是不相等的,包括它本身!
通过x!==x来判断x是否为NaN,只是在x在NaN的时候,这个表达式的值才为true;
var a1=NaN;
var a2=NaN;
console.log(a1===a2);//false
console.log(a1!==a2);//true


5. 谈谈浏览器window对象

1)window对象的方法:
    window.alert(msg);//警告框
    window.confirm(msg);//确认框
    window.prompt(msg,[,defaultText]);//提示框
    window.close();//关闭窗口
    window.print();//打印
    
    window.setInterval(表达式,毫秒);
    window.clearInterval(定时器对象);
    window.setTimeout(表达式,毫秒);
    window.clearTimeout(定时器对象);

2)window子对象-navigator(浏览器信息对象)
    appCodeName //内部代码
    appName //浏览器名称
    appVersion //浏览器版本
    platform //操作系统类型
    userAgent //用户代理信息
    cookieEnabled //是否支持cookie

3)window子对象-location(地址栏对象)
    host //主机
    port //端口
    href //地址
    pethname //路径
    protocal //协议
    search //查询字符串
    assign(url) //页面跳转

4)window子对象-history(历史记录)
    length //历史记录数目
    back(); //后退
    forward(); //前进
    go(); //跳转

5)window子对象-screen(屏幕对象)
    height //高度
    width //宽度
    avaiHeight //可用高度
    avaiWidth //可用宽度
    colorDepth //颜色

6. js获取对象的样式方法

1) ele.style -只能获取内联的样式,其他的都无法获取到!
<div style="width:300px;height:100px;" id="div1">测试</div>

var oDiv=document.getElementById("div1");
console.log(oDiv.style.width,oDiv.style.height); //300px,100px

2)obj.currentStyle和window.getComputedStyle()
注意:只有IE(IE9以下)和Opera支持使用obj.currentStyle来获取HTMLElement的计算后的样式,标准浏览器中使用window.getComputedStyle(),IE9及以上也支持
window.getComputedStyle();

参数说明:
obj.currentStyle['attr'] //attr为属性名称
window.getComputedStyle(obj,null)['attr'] //obj为目标元素,attr为属性名称

考虑兼容性,封装函数
function getStyle(el,attr){
    return el.currentStyle?el.currentStyle[attr]:window.getComputedStyle(el,null)[attr]
}


7. 谈谈js之节点

1).根据 W3C HTML DOM 标准,HTML 文档中的所有事物都是节点:
    a).整个文档是文档节点
    b).每个HTML元素是元素节点
    c).HTML元素内的文本是文本节点
    d).每个HTML属性是属性节点
    e).所有注释是注释节点

2).节点访问关系
    a).parentNode //访问某个节点的父节点 
    b).childNodes //得到某个节点的所有子节点,IE9以下还会包含文本节点(空格和换行)
    c).children //得到某个节点中的所有元素子元素
<ul id="uParent">
    <li>元素1</li>
    <li>元素2</li>
    <li>元素3</li>
</ul>

var oUl=document.getElementById("uParent");
console.log(oUl.childNodes.length) //7
console.log(oUl.children.length) //3
    d).firstChild  // IE9及以上得到第一个元素节点,包含文本节点(空格和换行);IE9以下得到第一个元素节点;
    
    e).firstElementChild // IE9及以上得到第一个元素节点;IE9及以上得到第一个元素节点;
    
    f).lastChild //IE9及以上得到最后一个元素节点,包含文本节点(空格和换行);IE9以下得到最后一个元素节点;
    
    g).lastElementChild //IE9及以上得到最后一个元素节点;IE9以下不支持;
    
    h).nextSibling //IE9及以上获取后一个兄弟节点对象,包含文本节点(空格和换行);IE9以下获取后一个兄弟节点对象;
    
    j).nextElementSibling //IE9及以上获取后一个兄弟节点对象;IE9以下不支持;
    
    k).previousSibling //IE9及以上获取前一个兄弟节点对象,包含文本节点(空格和换行);IE9以下获取前一个兄弟节点对象;
    
    l).previousElementSibling //IE9及以上获取前一个兄弟节点对象;IE9以下不支持;

8. 谈谈js之节点操作

    1)document.createElement() //在对象中创建一个对象,参数为元素的标签名
    
    2)document.createTextNode() //在对象中创建一个文本节点,参数为所要生成的文本节点的内容
    
    3)element.appendChild() //向节点的子节点列表的末尾添加新的子节点
    
    4)element.hasChildNodes() //返回一个布尔值,表示当前节点是否有子节点
    
    5)element.removeChild() //从子节点列表中删除某个节点
    
    6)element.cloneNode() //克隆某个元素
    
    7)element.insertBefore() //现有的子元素之前插入一个新的子元素
    
    8)element.replaceChild() //替换一个子元素
    var aE = document.createElement("div");
    aE.className = "cla";
    var aT=document.createTextNode("1.这是创建添加元素");
    aE.appendChild(aT);
    document.body.appendChild(aE);
    
    console.log(aE.hasChildNodes("div")); //true

9. 谈谈js作用域

1).JS作用域的特点:首先在函数内部查找变量,查不到则到外层函数查找,逐步找到最外层,即window对象,并操作window对象的属性
```
console.log(window.a,window.b); 
function t(){
    var a='local';
    b='global';
}
t();
console.log(window.a,window.b);

=>
    undefined undefined
    undefined "global"
    

var c=5;
function t1(){
var d=6;
    function t2(){
        var d=3;
        var e=7;
        console.log(c+d+e);
    }
    t2();
}
t1();//=>15; (5+3+7)

```

10. 总结js查找元素的方法:

    1)按id查找:document.getElementById("iD1");
    2)按类名查找:document.getElementsByClassName("class1")[0];
    3)按标签查找:document.getElementsByTagName("div");
    4)表单元素按那么来查找:doucment.getElementsByName('name')[0];
                          document.getElementsByName('name')[0].value

11. 谈谈js中的arguments:

1)arguments:是一个对象,内容是函数运行时的实参列表
(function(a,b,c){
    console.lof(arguments); // =>对象
    console.lof(arguments[0]); // => hello
    console.lof(arguments[2]); // => !
})("hello","world","!")

2)arguments.callee() 属性代表 “当前运行的函数”
alert((function(n){ //不用函数名,匿名函数,立即执行,完成递归
    if(n<=1){
        return 1;
    }else{
        return n+arguments.callee(n-1);
    }
})(100))  // => 5050

12. 谈谈css盒子模型的理解:

css盒模型由两个盒子组成,外在的控制是否换行的盒子,以及内在的控制元素内容的盒子。
比如:display: inline-block,则它的外在的盒子就是inline也就是不占据一行,
而block则表示内部的元素具有块状特性。所以,display: inline其实就是
display:inline-inline的缩写,display: block就是display: block-block的缩写。

每一个内在的盒子有: width/height, padding, border, margin这几个控制盒子大小的属性。
其中 width/height控制元素内容大小,padding则控制元素内容到border线内侧距离,
border则是元素外围边框大小,而margin则是控制与其他元素的间距,它的背景透明。

对于早期,计算一个元素的占据大小,需要通过width +2* padding + 2*border
来计算,css3中提出了box-sizing:border-box,通过这样设置,
就可以使元素最终的宽高就是设定的width/height, 浏览器会根据width/height, 
padding, border的大小来自动调整内部元素的大小。

13. 谈谈js三大对象:

JavaScript有3大对象 分别是本地对象、内置对象和宿主对象

  • 本地对象
    • 与宿主无关,独立于宿主环境的ECMAScript实现提供的对象
    • 简单来说,本地对象就是ECMA-262定义的类(引用类型)。
    • 这些引用类型在运行过程中需要通过new来创建所需的实例对象。
    • 包含:Object Array Date RegExp Function Boolean Number String等
  • 内置对象
    • 与宿主无关,独立于宿主环境的ECMAScript实现提供的对象
    • 在ECMAScript 程序开始执行前就存在,本身就是实例化内置对象,开发者无需再去实例化。
    • 内置对象是本地对象的子集
    • 包含:Global和Math
    • ECMAScript5中增添了JSON这个存在于全局的内置对象
  • 宿主对象
    • 由ECMAScript 实现的宿主环境提供的对象,包含两大类,一个死宿主提供,一个是自定义类对象。
    • 所有非本地对象都属于宿主对象
    • 对于嵌入到网页中的JS来说,其宿主对象就是浏览器提供的对象,浏览器对象有很多,如window和document等
    • 所有的DOM和BOM对象都属于对象

14. 谈谈js内置对象Object:

Object对象都有一个实例方法和一个静态方法(constructor)

  • 实例方法

    • hasOwnProperty:会返回一个布尔值,指示对象自身属性中是否具有指定的属性
        o = new Object();
        o.prop = 'exists';
        
        function changeO() {
            o.newprop = o.prop;
            delete o.prop;
        }
        
        o.hasOwnProperty('prop');   // 返回 true
        changeO();
        o.hasOwnProperty('prop');   // 返回 false
    
    • isPrototypeOf:方法用于测试一个对象是否存在于另一个对象的原型链上。
        function Foo() {}
        function Bar() {}
        function Baz() {}
        
        Baz.prototype = Object.create(Bar.prototype);
        var baz = new Baz();
    
        console.log(Baz.prototype.isPrototypeOf(baz)); // true
        console.log(Bar.prototype.isPrototypeOf(baz)); // true
        console.log(Foo.prototype.isPrototypeOf(baz)); // false
        console.log(Object.prototype.isPrototypeOf(baz)); // true
    
    • propertyIsEnumerable:方法返回一个布尔值,表示指定的属性是否可枚举。
        function Person(){
            this.name="我是实例属性";
            this.age=19;
        }
        var p=new Person();
        console.log(p.propertyIsEnumerable("name")); //true
    
    
    • toLocaleString : 返回当前对象的"本地化"字符串形式,以便于当前环境的用户辨识和使用,返回值为String类型
        (1234567).toLocaleString(); //"1,234,567"
        (6.37588).toLocaleString(); //"6.376"
        (new Date()).toLocaleString(); //"2017/9/24 下午2:58:21"
    
    • toString :返回当前对象的字符串形式,返回值为String类型
        [1,'2',true].toString(); //"1,2,true"
        (new Date()).toString(); //"Tue Jul 23 2019 14:54:47 GMT+0800 (中国标准时间)"
    
    • valueOf :返回指定对象的原始值
    a=? 使得 a==1 && a==2 && a==3 成立
    
    var a={
        i=1;
        valueOf:function(){
            return a.i++;
        }
    }
    
  • 静态方法

    • assign:把一个或多个源对象的可枚举、自有属性值复制到目标对象中,返回值为目标对象。
    var target = {
        a:1
    };
    var source1 = {
        b:2
    };
    var source2 = {
        c:function(){
          console.log('c');
        }
    };
    Object.assign(target,source1,source2);
    console.log(target); //{a: 1, b: 2, c: ƒ}
    
    • create:创建一个对象,其原型为prototype,同时可添加多个属性。
    const person = {
      isHuman: false,
      printIntroduction: function () {
        console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
      }
    };
    
    const me = Object.create(person);
    
    me.name = "Matthew"; // "name" is a property set on "me", but not on "person"
    me.isHuman = true; // inherited properties can be overwritten
    
    me.printIntroduction();
    // expected output: "My name is Matthew. Am I human? true"
    
    • defineProperties:在一个对象上定义一个或多个新属性或修改现有属性,并返回该对象。
    var obj = {};
    Object.defineProperties(obj,{
        name:{
          writable: true,
          configurable: true,
          enumerable: false,
          value: '张三'
        },
        age:{
          writable: true,
          configurable: true,
          enumerable: true,
          value: 23
        }
    });
    
    console.log(obj.name); //'张三'
    console.log(obj.age); //23
    for(var key in obj){
        console.log(obj[key]); //23
    }
    
    • defineProperty:在一个对象上定义一个新属性或修改一个现有属性,并返回该对象。
    var obj = {};
    Object.defineProperty(obj,'name',{
        writable: true,
        configurable: true,
        enumerable: false,
        value: '张三'
    });
    
    console.log(obj.name); //'张三'
    for(var key in obj){
        console.log(obj[key]); //无结果
    }
    
    • entries:方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。
    const object1 = {
      a: 'somestring',
      b: 42
    };
    
    for (let [key, value] of Object.entries(object1)) {
      console.log(`${key}: ${value}`);
    }
    > "a: somestring"
    > "b: 42"
    
    • freeze:完全冻结对象,在seal的基础上,属性值也不可以修改,即每个属性的wirtable也被设为false。
    var obj = {name:'张三'};
    
    Object.freeze(obj);
    console.log(Object.isFrozen(obj)); //true
    
    obj.name = '李四'; //修改值失败
    console.log(obj.name); //'张三'
    obj.age = 23; //无法添加新属性
    console.log(obj.age); //undefined
    
    Object.defineProperty(obj,'name',{ 
        writable: true,
        configurable: true,
        enumerable: true
    }); //报错:Cannot redefine property: name
    
    • getOwnPropertyDescriptor :获取目标对象上某自有属性的配置特性(属性描述符),返回值为配置对象。
    var obj = {};
    
    Object.defineProperty(obj,'name',{
        writable: true,
        configurable: false,
        enumerable: true,
        value: '张三'
    });
    
    var prop = Object.getOwnPropertyDescriptor(obj,'name');
    console.log(prop); //{value: "张三", writable: true, enumerable: true, configurable: false}
    
    • getOwnPropertyDescriptors:方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
    var o, d;
    
    o = { get foo() { return 17; } };
    d = Object.getOwnPropertyDescriptor(o, "foo");
    // d {
    //   configurable: true,
    //   enumerable: true,
    //   get: /*the getter function*/,
    //   set: undefined
    // }
    
    o = { bar: 42 };
    d = Object.getOwnPropertyDescriptor(o, "bar");
    // d {
    //   configurable: true,
    //   enumerable: true,
    //   value: 42,
    //   writable: true
    // }
    
    o = {};
    Object.defineProperty(o, "baz", {
      value: 8675309,
      writable: false,
      enumerable: false
    });
    d = Object.getOwnPropertyDescriptor(o, "baz");
    // d {
    //   value: 8675309,
    //   writable: false,
    //   enumerable: false,
    //   configurable: false
    // }
    
    • getOwnPropertyNames:获取目标对象上的全部自有属性名(包括不可枚举属性)组成的数组。
    var obj = {};
    obj.say = function(){};
    
    Object.defineProperties(obj,{
        name:{
          writable: true,
          configurable: true,
          enumerable: true,
          value: '张三'
        },
        age:{
          writable: true,
          configurable: true,
          enumerable: false,
          value: 23
        }
    });
    
    var arr = Object.getOwnPropertyNames(obj);
    console.log(arr); //["say", "name", "age"]
    
    • getOwnPropertySymbols:方法返回一个给定对象自身的所有 Symbol 属性的数组。
    var obj = {};
    var a = Symbol("a");
    var b = Symbol.for("b");
    
    obj[a] = "localSymbol";
    obj[b] = "globalSymbol";
    
    var objectSymbols = Object.getOwnPropertySymbols(obj);
    
    console.log(objectSymbols.length); // 2
    console.log(objectSymbols)         // [Symbol(a), Symbol(b)]
    console.log(objectSymbols[0])      // Symbol(a)
    
    • getPrototypeOf:获取指定对象的原型,即目标对象的prototype属性的值。
    function Person(name){
        this.name = name;
    }
    
    var person = new Person('张三');
    var p = Object.create(person); //对象p的原型为person
    console.log(p); //Person {}
    
    var __ptoto__ = Object.getPrototypeOf(p);
    console.log(__ptoto__); //Person {name: "张三"}
    
    • is:方法判断两个值是否是相同的值。 Object.is() 判断两个值是否相同。如果下列任何一项成立,则两个值相同:

        两个值都是 undefined
        两个值都是 null
        两个值都是 true 或者都是 false
        两个值是由相同个数的字符按照相同的顺序组成的字符串
        两个值指向同一个对象
        两个值都是数字并且
        都是正零 +0
        都是负零 -0
        都是 NaN
        都是除零和 NaN 外的其它同一个数字
        这种相等性判断逻辑和传统的 == 运算不同,== 运算符会对它两边的操作数做隐式类型转换(如果它们类型不同),然后才进行相等性比较,(所以才会有类似 "" == false 等于 true 的现象),但 Object.is 不会做这种类型转换。
        
        这与 === 运算符的判定方式也不一样。=== 运算符(和== 运算符)将数字值 -0 和 +0 视为相等,并认为 Number.NaN 不等于 NaN。
      
      Object.is('foo', 'foo');     // true
      Object.is(window, window);   // true
      
      Object.is('foo', 'bar');     // false
      Object.is([], []);           // false
      
      var foo = { a: 1 };
      var bar = { a: 1 };
      Object.is(foo, foo);         // true
      Object.is(foo, bar);         // false
      
      Object.is(null, null);       // true
      
      // 特例
      Object.is(0, -0);            // false
      Object.is(0, +0);            // true
      Object.is(-0, -0);           // true
      Object.is(NaN, 0/0);         // true
      
    • isExtensible:用于判断一个对象是否可扩展,即是否可以添加新属性。

    var obj = {
      name: '张三'
    };
    Object.preventExtensions(obj); //阻止obj的可扩展性
    console.log(Object.isExtensible(obj)); //false,表明obj对象为不可扩展,即阻止成功
    
    • isFrozen:Object.isFrozen(obj)用于判断目标对象是否被冻结,返回布尔值。

    • isSealed:用于判断目标对象是否被密封,返回布尔值。

    • keys:获取目标对象上所有可枚举属性组成的数组。

    var person = {
        type:'person',
        say:function(){}
      };
    var arr = [];
      for(var key in person){
        arr.push(key);
      }
      console.log(arr); //["type", "say"]
    
    
    • preventExtensions:使某一对象不可扩展,也就是不能为其添加新属性。
    var obj = {
      name: '张三'
    };
    Object.preventExtensions(obj); //阻止obj的可扩展性
    
    • prototype:属性表示 Object 的原型对象

    • seal:密封对象,阻止其修改现有属性的配置特性,即将对象的所有属性的configurable特性设置为false,并阻止添加新属性,返回该对象。

    var obj = {name:'张三'};
    
    Object.seal(obj);
    console.log(Object.isSealed(obj)); //true
    
    obj.name = '李四'; //修改值成功
    console.log(obj.name); //'李四'
    obj.age = 23; //无法添加新属性
    console.log(obj.age); //undefined
    
    Object.defineProperty(obj,'name',{ 
        writable: true,
        configurable: true,
        enumerable: true
    }); //报错:Cannot redefine property: name
    
    • setPrototypeOf:设置目标对象的原型为另一个对象或null,返回该目标对象。
    var obj = {a:1};
    var proto = {};
    Object.setPrototypeOf(obj,proto); //设置obj对象的原型
    
    proto.b = 2; //为该原型对象添加属性
    proto.c = 3;
    
    console.log(obj.a); //1
    console.log(obj.b); //2
    console.log(obj.c); //3
    
    • values:方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
    var obj = { foo: 'bar', baz: 42 };
    console.log(Object.values(obj)); // ['bar', 42]
    

15. 谈谈html中的置换元素和非置换元素的理解:

  • 置换元素是指:浏览器根据元素的标签和属性,来决定元素的具体显示内容。例如:浏览器根据标签的src属性显示图片。根据标签的type属性决定显示输入框还是按钮。
  • 非置换元素:浏览器中的大多数元素都是不可置换元素,即其内容直接展示给浏览器。例如标签, 标签里的内容会被浏览器直接显示给用户。

16. 谈谈js内置对象Array :

  • 对象方法
    • concat: 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
    var array1 = ['a', 'b', 'c'];
    var array2 = ['d', 'e', 'f'];
    
    console.log(array1.concat(array2)); //["a", "b", "c", "d", "e", "f"]
    
    copyWithin:方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
    语法:arr.copyWithin(target[, start[, end]])
    
    target:
        0 为基底的索引,复制序列到该位置。如果是负数,target 将从末尾开始计算。
        如果 target 大于等于 arr.length,将会不发生拷贝。如果 target 在 start 之后,复制的序列将被修改以符合 arr.length。
    
    start
    
        0 为基底的索引,开始复制元素的起始位置。如果是负数,start 将从末尾开始计算。
        如果 start 被忽略,copyWithin 将会从0开始复制。
    
    end
        0 为基底的索引,开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数, end 将从末尾开始计算。
        如果 end 被忽略,copyWithin 方法将会一直复制至数组结尾(默认为 arr.length)。
        
    例子:
    let numbers = [1, 2, 3, 4, 5];
    
    numbers.copyWithin(-2);
    // [1, 2, 3, 1, 2]
    //'start' 被忽略,copyWithin 将会从0开始复制。
    
    numbers.copyWithin(0, 3);
    // [4, 5, 3, 4, 5]
    
    numbers.copyWithin(0, 3, 4); //将3号位复制到0号位
    // [4, 2, 3, 4, 5]
    
    • entries: 方法返回一个新的数组迭代器对象,该对象包含数组中每个索引的键/值对。
    let arr = ["a","b","c"];
    for(let [index,item] of arr.entries()){console.log(index,item)};
    
    //输出:
    0 "a"
    1 "b"
    2 "c"
    
    • every:方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
    [12, 5, 8, 130, 44].every(x => x >= 10); // false
    [12, 54, 18, 130, 44].every(x => x >= 10); // true
    
    • fill:方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
    语法:arr.fill(value[, start[, end]])
        value:
            用来填充数组元素的值。
        start:(可选)
            起始索引,默认值为0。
        end :(可选)
        终止索引,默认值为 this.length。
        
        例子:
        [1, 2, 3].fill(4);               // [4, 4, 4]
        [1, 2, 3].fill(4, 1);            // [1, 4, 4]
    
    • filter:方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
    语法:var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
        callback
            用来测试数组的每个元素的函数。返回 true 表示该元素通过测试,保留该元素,false 则不保留。它接受以下三个参数:
            element
                数组中当前正在处理的元素。
            index可选
                正在处理的元素在数组中的索引。
            array可选
                调用了 filter 的数组本身。
        thisArg可选
        执行 callback 时,用于 this 的值。
        
        例子:
        function isBigEnough(element) {
          return element >= 10;
        }
        var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
        // filtered is [12, 130, 44]
    
    • find:方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
    var array1 = [5, 12, 8, 130, 44];
    var found = array1.find(function(element) {
      return element > 10;
    });
    
    console.log(found);
    //12
    
    • findIndex:方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。
    var array1 = [5, 12, 8, 130, 44];
    var found = array1.findIndex(function(element) {
      return element > 10;
    });
    
    console.log(found);
    //1
    
    • flat:方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
    var arr1 = [1, 2, [3, 4]];
    arr1.flat();
    // [1, 2, 3, 4]
    
    • flatMap:方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
    var arr1 = [1, 2, 3, 4];
    
    arr1.map(x => [x * 2]); 
    // [[2], [4], [6], [8]]
    
    arr1.flatMap(x => [x * 2]);
    // [2, 4, 6, 8]
    
    • forEach:方法对数组的每个元素执行一次提供的函数。
    var array1 = ['a', 'b', 'c'];
    
    array1.forEach(function(element) {
      console.log(element);
    });
    
    //"a","b", "c"
    
    • includes:方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
    var array1 = [1, 2, 3];
    
    console.log(array1.includes(2));//true
    
    • indexOf:方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
    var beasts = ['ant', 'bison', 'camel', 'duck', 'bison'];
    
    console.log(beasts.indexOf('bison')); //1
    
    • join:方法通过将数组(或类似数组的对象)中的所有元素连接起来,用逗号或指定的分隔符字符串分隔,创建并返回一个新字符串。如果数组只有一个项,那么该项将在不使用分隔符的情况下返回。
    var elements = ['Fire', 'Air', 'Water'];
    
    console.log(elements.join());//Fire,Air,Water
    
    • keys:方法返回一个包含数组中每个索引键的Array Iterator对象。
    var array1 = ['a', 'b', 'c'];
    var iterator = array1.keys(); 
      
    for (let key of iterator) {
      console.log(key); // 0 1 2
    }
    
    • lastIndexOf:方法返回数组中可以找到给定元素的最后一个索引,如果不存在,则返回-1。数组从fromIndex开始向后搜索。
    var animals = ['Dodo', 'Tiger', 'Penguin', 'Dodo'];
    
    console.log(animals.lastIndexOf('Dodo'));//3
    
    • map:方法对调用数组中的每个元素调用提供的函数的结果创建一个新数组。
    var array1 = [1, 4, 9, 16];
    const map1 = array1.map(x => x * 2);
    
    console.log(map1);//[2, 8, 18, 32]
    
    • pop:方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
    var plants = ['broccoli', 'cauliflower', 'cabbage', 'kale', 'tomato'];
    plants.pop();
    
    console.log(plants);//["broccoli", "cauliflower", "cabbage", "kale"]
    
    • push:方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
    var animals = ['pigs', 'goats', 'sheep'];
    
    console.log(animals.push('cows')); //["pigs", "goats", "sheep", "cows"]
    
    • reduce:方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
    语法:arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
    
    callback
    执行数组中每个值的函数,包含四个参数:
        accumulator
            累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
    
        currentValue
            数组中正在处理的元素。
            
        currentIndex(可选)
            数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则为1。
            
        array(可选)
            调用reduce()的数组
            
    initialValue(可选)
    作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
    
    例子:
    var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) {
        return accumulator + currentValue;
    }, 0); //6
    
    • reduceRight:方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
    const array1 = [[0, 1], [2, 3], [4, 5]].reduceRight(
    (accumulator, currentValue) => accumulator.concat(currentValue)
    );
    
    console.log(array1); //[4, 5, 2, 3, 0, 1]
    
    • reverse:方法将数组中元素的位置颠倒,并返回该数组。该方法会改变原数组。
    var array1 = ['one', 'two', 'three'];
    console.log('array1: ', array1); //['one', 'two', 'three']
    
    • shift:方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
    var array1 = [1, 2, 3];
    var firstElement = array1.shift();
    
    console.log(array1);[2, 3]
    
    • slice:方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
    语法:arr.slice([begin[, end]])
    
    var animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
    
    console.log(animals.slice(2)); //["camel", "duck", "elephant"]
    
    • some:方法测试是否至少有一个元素可以通过被提供的函数方法。该方法返回一个Boolean类型的值。
    [2, 5, 8, 1, 4].some(x => x > 10);  // false
    
    • sort:方法用原地算法对数组的元素进行排序,并返回数组。
    var numbers = [4, 2, 5, 1, 3];
    numbers.sort(function(a, b) {
      return a - b;
    });
    
    console.log(numbers); // [1, 2, 3, 4, 5]
    
    • splice:方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。
    语法:array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
    
    start​
    指定修改的开始位置(从0计数)。如果超出了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于array.length-n);如果负数的绝对值大于数组的长度,则表示开始位置为第0位。
    
    deleteCount 可选
    整数,表示要移除的数组元素的个数。
    如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。
    如果 deleteCount 被省略了,或者它的值大于等于array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。
    如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。
    item1, item2, ... 可选要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。
    
    
    var months = ['Jan', 'March', 'April', 'June'];
    months.splice(1, 0, 'Feb');
    
    console.log(months);// ['Jan', 'Feb', 'March', 'April', 'June']
    
    • toString:返回一个字符串,表示指定的数组及其元素。
    var array1 = [1, 2, 'a', '1a'];
    
    console.log(array1.toString()); //"1,2,a,1a"
    
    • unshift:方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。
    var array1 = [1, 2, 3];
    array1.unshift(4, 5);
    
    console.log(array1); // [4, 5, 1, 2, 3]
    
    • values:方法返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值。
    const array1 = ['a', 'b', 'c'];
    const iterator = array1.values();
    
    for (const value of iterator) {
      console.log(value); // "a" "b" "c"
    }
    

17. 谈谈js内置对象String :

  • 对象属性

    • length: 属性表示一个字符串的长度。
  • 对象方法

    • anchor:方法创建一个a锚元素,被用作超文本靶标。
    var myString = "Table of Contents";
    document.body.innerHTML = myString.anchor("contents_anchor");
    

    • big:方法的作用是创建一个使字符串显示大号字体的'big'标签
    'big'元素在HTML5中已经被移除了,不应该再使用它。 取而代之的是web开发人员应该使用CSS 属性。
    
    var worldString = 'Hello, world';
    console.log(worldString.big());  // <big>Hello, world</big>
    
    • charAt:方法从一个字符串中返回指定的字符。
    var anyString = "Brave new world";
    console.log(anyString.charAt(0)); //'B'
    
    • charCodeAt:方法返回0到65535之间的整数,表示给定索引处的UTF-16代码单元。
    "ABC".charCodeAt(0) // 65
    
    • codePointAt:方法返回 一个 Unicode 编码点值的非负整数。
    'ABC'.codePointAt(1);// 66
    
    • concat:方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。
    var hello = "Hello, ";
    console.log(hello.concat("Kevin", " have a nice day.")); /* Hello, Kevin have a nice day. */
    
    • endsWith:方法用来判断当前字符串是否是以另外一个给定的子字符串“结尾”的,根据判断结果返回 true 或 false。
    const str1 = 'Cats are the best!';
    
    console.log(str1.endsWith('best', 17)); //true
    
    • includes:方法用于判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false。
    var str = 'To be, or not to be, that is the question.';
    
    console.log(str.includes('To be'));       // true
    
    • indexOf:方法返回调用它的 String 对象中第一次出现的指定值的索引,从 fromIndex 处进行搜索。如果未找到该值,则返回 -1。
    "Blue Whale".indexOf("Blue");  // 返回  0
    
    • lastIndexOf:方法返回指定值在调用该方法的字符串中最后出现的位置,如果没找到则返回 -1。从该字符串的后面向前查找,从 fromIndex 处开始。
    "canal".lastIndexOf("a")   // returns 3
    
    • localeCompare:方法返回一个数字来指示一个参考字符串是否在排序顺序前面或之后或与给定字符串相同。

    • match:方法检索返回一个字符串匹配正则表达式的的结果。

    语法:str.match(regexp)
    例子:
    var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    var regexp = /[A-E]/gi;
    var matches_array = str.match(regexp);
    
    console.log(matches_array);
    // ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']
    
    • normalize:方法会按照指定的一种 Unicode 正规形式将当前字符串正规化。

    • padEnd:方法会用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充。

    'abc'.padEnd(10, "foo");   // "abcfoofoof"
    'abc'.padEnd(6, "123456"); // "abc123"
    
    • padStart:方法用另一个字符串填充当前字符串(重复,如果需要的话),以便产生的字符串达到给定的长度。填充从当前字符串的开始(左侧)应用的。
    'abc'.padStart(10);         // "       abc"
    'abc'.padStart(10, "foo");  // "foofoofabc"
    
    • repeat:构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。
    "abc".repeat(1)      // "abc"
    "abc".repeat(2)      // "abcabc"
    "abc".repeat(3.5)    // "abcabcabc" 参数count将会被自动转换成整数
    
    • replace:方法返回一个由替换值(replacement)替换一些或所有匹配的模式(pattern)后的新字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的回调函数。
    var p = 'The dog';
    
    var regex = /dog/gi;
    
    console.log(p.replace(regex, 'cat'));//"The cat"
    
    • search:方法执行正则表达式和 String 对象之间的一个搜索匹配。
    var str = "hey JudE";
    var re = /[A-Z]/g;
    console.log(str.search(re)); // "J"
    
    • slice:方法提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串。
    var str = 'The quick brown fox jumps over the lazy dog.';
    console.log(str.slice(31)); //"the lazy dog."
    
    • split:方法使用指定的分隔符字符串将一个String对象分割成字符串数组,以将字符串分隔为子字符串,以确定每个拆分的位置。
    var str = 'The quick brown fox jumps over the lazy dog.';
    var words = str.split(' ');
    
    console.log(words[3]); //"fox"
    
    • startsWith:方法用来判断当前字符串是否以另外一个给定的子字符串开头,并根据判断结果返回 true 或 false。
    const str1 = 'Saturday night plans';
    
    console.log(str1.startsWith('Sat')); // true
    
    • substring:方法返回一个字符串在开始索引到结束索引之间的一个子集,或从开始索引直到字符串的末尾的一个子集。
    var anyString = "Mozilla";
    console.log(anyString.substring(0,3)); //Moz
    
    • toLocaleLowerCase/toLowerCase:方法根据任何特定于语言环境的案例映射,返回调用字符串值转换为小写的值。
    console.log('ALPHABET'.toLocaleLowerCase()); // 'alphabet'
    
    • toLocaleUpperCase/toUpperCase:方法根据任何特定于语言环境的案例映射,返回调用字符串值转换为大写的值。
    console.log('alphabet'.toLocaleLowerCase()); // 'ALPHABET'
    
    • toString:方法返回指定对象的字符串形式。
    var x = new String("Hello world");
    console.log(x.toString())      // 输出 "Hello world"
    
    • trim:方法会从一个字符串的两端删除空白字符。在这个上下文中的空白字符是所有的空白字符 (space, tab, no-break space 等) 以及所有行终止符字符(如 LF,CR)。
    var orig = '   foo  ';
    console.log(orig.trim()); // 'foo'
    
    • trimEnd:方法从一个字符串的右端移除空白字符。trimRight()是此方法的别名
    var greeting = '   Hello world!   ';
    console.log(greeting.trimEnd());// "   Hello world!"
    
    • trimLeft:方法从字符串的开头删除空格。trimLeft()是此方法的别名。
    var greeting = '   Hello world!   ';
    console.log(greeting.trimStart());// "Hello world!   "
    
    • valueOf:方法返回一个String对象的原始值(primitive value)。
    var x = new String("Hello world");
    console.log(x.valueOf()) // "Hello world"
    

    18. 谈谈js数据推送:

    • Comet
      Comet模式是一种服务器端推技术,它的核心思想提供一种能让当服务器端往客户端发送数据的方式。
      由服务器端通过长连接推数据,能大大减少发送到服务器端的请求从而避免了很多开销,而且它还具备更好的实时性。
      
    • websocket (H5新特性)
      浏览器通过 JavaScript 向服务器发出建立 WebSocket连接的请求,
      连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
      也就是我们可以使用web技术构建实时性的程序比如聊天游戏等应用。
      
    • sse
      SSE API用于创建到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据。
      服务器响应的MIME类型必须是text/event-stream,
      而且是浏览器中的Javascript API能解析的格式输出。
      
    • 补充 -前后台交互方式
      • 服务端渲染
      • AJAX
      • JSONP:JSONP算作JSON的一种"使用模式",可用于解决主流浏览器的跨域数据访问的问题。由于CORS的支持,我们可以简单的将数据封装成一个js脚本请求,当然我们在jquery中会用到。
      function logResults(json){  
          console.log(json);
      }
      $.ajax({
          url: "https://xxxx.com/api",
          dataType: "jsonp",
          jsonpCallback: "logResults"
      });
      
    • 小结:
      • AJAX - 请求 → 响应 (频繁使用)

      • Comet - 请求 → 挂起 → 响应 (模拟服务端推送)

      • Server-Sent Events - 客户单 ← 服务端 (服务端推送)

      • WebSockets - 客户端 ↔ 服务端 (未来趋势,双工通信)

19. 谈谈前端模块化:

  • CommonJS

Node 应用由模块组成,采用 CommonJS 模块规范。 每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。

参考文档:javascript.ruanyifeng.com/nodejs/modu…

  • 特点:
    • 所有代码都运行在模块作用域,不会污染全局作用域。
    • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
    • 模块加载的顺序,按照其在代码中出现的顺序。
    • commonJS用同步的方式加载模块
    1.exports(即module.exports)是对外的接口
    
    // example.js
    var x = 5;
    var addX = function (value) {
      return value + x;
    };
    module.exports = { //在这里写上需要向外暴露的函数、变量
      x: x,
      addX: addX
    }
    
    2.require:方法用于加载模块
    
    var example = require('./example.js');
    
    console.log(example.x); // 5
    console.log(example.addX(1)); // 6
    
    
  • AMD Require.js

用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

参考文档:www.ruanyifeng.com/blog/2012/1…

1.格式:require([module], callback); 
第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。

例子:

// 定义math.js模块
define(function () {
    var add = function (x, y) {
        return x + y;
    };
    return {
        add: add,
    };
});

// 引用模块,将模块放在[]内
require(['jquery', 'math'],function($, math){
  var sum = math.add(3,4);
});
  • CMD Sea.js

实现模块定义规范,这是模块系统的基础;模块系统的启动与运行,推崇依赖就近、延迟执行。

// 定义模块 math.js
define(function(require, exports, module) {
    var $ = require('jquery.js');
    var add = function(a,b){
        return a+b;
    }
    exports.add = add;
});
// 加载模块
seajs.use(['math.js'], function(math){
    var sum = math.add(3,4);
});

  • SeaJS与RequireJS的主要区别

    • 遵循的规范不同:

      RequireJS 遵循 AMD(异步模块定义)规范,SeaJS 遵循 CMD (通用模块定义)规范。

    • factory 的执行时机不同

    SeaJS按需执行依赖避免浪费,但是require时才解析的行为对性能有影响。
        
    SeaJS是异步加载模块的没错, 但执行模块的顺序也是严格按照模块在代码中出现(require)的顺序。
    
    RequireJS更遵从js异步编程方式,提前执行依赖,输出顺序取决于哪个 js 先加载完(不过 RequireJS 从 2.0 开始,也改成可以延迟执行)。如果一定要让 模块B 在 模块A 之后执行,需要在 define 模块时申明依赖,或者通过 require.config 配置依赖。
    
    如果两个模块之间突然模块A依赖模块B:SeaJS的懒执行可能有问题,而RequireJS不需要修改当前模块。
    
    当模块A依赖模块B,模块B出错了:如果是SeaJS,模块A执行了某操作,可能需要回滚。RequireJS因为尽早执行依赖可以尽早发现错误,不需要回滚。
    
    • 聚焦点有差异

      SeaJS努力成为浏览器端的模块加载器,RequireJS牵三挂四,兼顾Rhino 和node,因此RequireJS比SeaJS的文件大。

    • 理念不一样

      RequireJS 有一系列插件,功能很强大,但破坏了模块加载器的纯粹性。SeaJS 则努力保持简单,并支持 CSS 模块的加载。

参考文档:aotu.io/notes/2016/…

  • ES6 Module

ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,旨在成为浏览器和服务器通用的模块解决方案。其模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

/** 定义模块 math.js **/
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };

/** 引用模块 **/
import { add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}
  • ES6 模块与 CommonJS 模块的差异

      1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
      1. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

参考文档:juejin.cn/post/684490…

20.谈谈跨域问题:

  • 什么是跨域:浏览器处于对安全方面的考虑,不允许跨域调用其他页面的对象。简单的说服务端和请求端的地址不一样。

  • 怎么解决跨域:

    • JSONP :

      • Web 页面上调用 js 文件不受浏览器同源策略的影响,所以通过 Script 便签可以进行跨域的请求;

      1.首先前端先设置好回调函数,并将其作为 url 的参数。
      2.服务端接收到请求后,通过该参数获得回调函数名,并将数据放在参数中将其返回
      3.收到结果后因为是 script 标签,所以浏览器会当做是脚本进行运行,从而达到跨域获取数据的目的。

      优点:兼容性很好,可以在古老的浏览器中国使用,因为这种方法是利用了script标签的特殊性,所有只支持GET请求

      //index.html
      <body>
          <script>
      	function jsonpCallback(data) {
      	    console.log('获得 X 数据:' + data.x); //获得 X 数据: 10
      	}
          </script>
          <script src="http://localhost:3000?callback=jsonpCallback"></script>
      </body>
      
      //server.js
      const url = require('url');
      const http = require('http');
      
      http.createServer((req, res)=>{
      const data = {
          x: 10//返回的数据
      };
      const callback = url.parse(req.url, true).query.callback;
      res.writeHead(200);
      res.end(`${callback}(${JSON.stringify(data)})`);
      //执行回调函数,返回data
      }).listen(3000, 'localhost');
      
      console.log('启动服务,监听 localhost:3000');
      
    • CORS :

      • 什么是CORS:CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 ajax 只能同源使用的限制。

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

      比较:CORS与JSONP的使用目的相同,但是比JSONP更强大。JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

      //index.html
      <script>
      $.ajax({
          url:"http://127.0.0.1:3000",
          success:function(res){
              var res = JSON.parse(res);
              $('body').text(res.data);
              console.log(res.data);
          }
      });
      </script>
      
      //server.js
      const http = require('http');
      http.createServer((req, res)=>{
      const data = {
          'data': 'Hello world'//返回的数据
      };
      res.writeHead(200, {'Access-Control-Allow-Origin': 'http://127.0.0.1:8080'});
      //设置的头部信息需要和前端请求的地址一致
      res.end(JSON.stringify(data));
      //返回data
      }).listen(3000, '127.0.0.1');
      
      console.log('启动服务,监听 127.0.0.1:3000');
      
    • Server Proxy :

      • 定义:服务器代理,顾名思义,当你需要有跨域的请求操作时发送请求给后端,让后端帮你代为请求,然后最后将获取的结果发送给你。
      //server.js
      const url = require('url');
      const http = require('http');
      const https = require('https');
      
      http.createServer((req, res)=>{
      const path = url.parse(req.url).path.slice(1);
      //核对请求路由是否一致
      if(path === 'topics'){
          https.get('https://cnodejs.org/api/v1/topics', (resp)=>{
              //https代发请求
              let data='';
              resp.on('data', chunk=>{
                  data+= chunk
              });
              resp.on('end', ()=>{
                  res.writeHead(
                      200,
                      {'Content-Type': 'application/json; charset=utf-8'}
                  );
                  res.end(data);
                  //返回数据
              })
          })
      }
      
      }).listen(3000, '127.0.0.1');
      console.log('启动服务,监听 127.0.0.1:3000');
      
      //index.html
      <script>
      $.ajax({
          url:"https://cnodejs.org/api/v1/topics",
          success:function(res){
              $('body').text(JSON.stringify(res));
              console.log(res);
          }
      });
      </script>
      

1.比较:
JSONP
优点是可以兼容老浏览器,缺点是只能发送GET请求
CORS
优点简单方便,支持post请求,缺点是需要后端的配合,不支持老版浏览器。。
Server Proxy
优点是前端正常发送ajax请求,缺点是后端会二次请求。

2.其他的跨域方式还有:location.hash、window.name、postMessage等方式

21.谈谈CSS优化,提高性能的方法?

  • 加载性能

    • 压缩CSS
    • 通过link方式加载,而不是@import
    • 复合属性其实分开写,执行效率更高,因为CSS最终也还是要去解析如 margin-left: left;
  • 选择器性能

    • 尽量少的使用嵌套,可以采用BEM的方式来解决命名冲突
    • 尽量少甚至是不使用标签选择器,这个性能实在是差,同样的还有*选择器
    • 利用继承,减少代码量
  • 渲染性能

    • 慎重使用高性能属性:浮动、定位;
    • 尽量减少页面重排、重绘;
    • css雪碧图
    • 自定义web字体,尽量少用
    • 尽量减少使用昂贵属性,如box-shadow/border-radius/filter/透明度/:nth-child等
    • 使用transform来变换而不是宽高等会造成重绘的属性

结尾

如果文章中有误的,可以留言告知。希望这篇文章对你有帮助!一起成长!一起进步!