Giter Site home page Giter Site logo

work's People

Contributors

zhaipanyu avatar

Watchers

 avatar  avatar

work's Issues

js和HTML的交互事件

为什么学习js和HTML的交互事件? 因为:当文档、浏览器、元素或与之相关对象发生特定事情时,浏览器会产生事件。如果JavaScript关注特定类型事件,那么它可以注册当这类事件发生时要调用的句柄事件是某个行为或者触发. 比如点击、鼠标移动 当用户点击鼠标时 当网页已加载时 当图像已加载时 当鼠标移动到元素上时 当用户触发按键时...

题目1: DOM0 事件和DOM2级在事件监听使用方式上有什么区别?

  • DOM0 事件
    1.通过JavaScript指定事件处理程序就是把一个方法赋值给一个元素的事件处理程序属性。
    2.每个元素都有自己的事件处理程序属性,这些属性名称通常为小写,如onclick等,将这些属性的值设置为一个函数,就可以指定事件处理程序
    3.删除事件处理程序,只需把元素的onclick属性赋为null即可.
<input id="btnClick" type="button" value="Click Here" />

<script type="text/javascript">
    var btnClick = document.getElementById('btnClick');
    btnClick.onclick = function showMessage() {
        alert(this.id);
    };
// btn.onclick = null;解绑 :不在监听
</script>

  • DOM2级事件:定义了两个方法用于处理指定和删除事件处理程序的操作:addEventListener,removeEventListener
    所有的DOM节点都包含这两个方法,并且它们都接受三个参数:
    事件类型 事件处理方法 布尔参数,true是在捕获阶段调用事件处理程序,false是在事件冒泡阶段处理
    刚才的例子我们可以这样写:
<input id="btnClick" type="button" value="Click Here" />

<script type="text/javascript">
    var btnClick = document.getElementById('btnClick');
    btnClick.addEventListener('click', function() {
        alert(this.id);
    }, false);
</script>

上面代码为button添加了click事件的处理程序,在冒泡阶段触发,与上一种方法一样,这个程序也是在元素的作用域下运行.
也可以为click事件添加多个处理程序比如下面的代码

<input id="btnClick" type="button" value="Click Here" />

<script type="text/javascript">
    var btnClick = document.getElementById('btnClick');

    btnClick.addEventListener('click', function() {
        alert(this.id);
    }, false);

    btnClick.addEventListener('click', function() {
        alert('Hello!');
    }, false);
</script>

这样两个事件处理程序会在用户点击button后按照添加顺序依次执行。

通过addEventListener添加的事件处理程序只能通过removeEventListener移除,移除时参数与添加的时候相同

  • 为什么DOM2级事件添加多级事件不会被覆盖?
    因为:DOM0级是赋值操作,一个指针的操作,DOM2级是给一个dom节点绑定了多个方法.

题目2: attachEvent与addEventListener的区别?

  • 为什么有以上两种添加事件的方法?
    因为IE兼容性和跨浏览器的事件处理程序

  • attachEvent是IE特有的方法
    :方法接收两个相同的参数:事件处理程序名称和事件处理程序方法
    使用attachEvent添加的事件处理程序可以通过detachEvent移除,条件也是相同的参数,匿名函数不能被移除。

  • 添加事件处理程序事addEventListenerattachEvent主要有4个区别

1.参数个数不相同,这个最直观,addEventListener有三个参数,attachEvent只有两个,attachEvent添加的事件处理程序只能发生在冒泡阶段,addEventListener第三个参数可以决定添加的事件处理程序是在捕获阶段还是冒泡阶段处理(我们一般为了浏览器兼容性都设置为冒泡阶段)

2.第一个参数意义不同,addEventListener第一个参数是事件类型(比如click,load),而attachEvent第一个参数指明的是事件处理函数名称(onclick,onload)

3.事件处理程序的作用域不相同,addEventListener的作用域是元素本身,this是指的触发元素,而attachEvent事件处理程序会在全局变量内运行,this是window,所以刚才例子才会返回undefined,而不是元素id

4.为一个事件添加多个事件处理程序时,执行顺序不同,addEventListener添加会按照添加顺序执行,而attachEvent添加多个事件处理程序时顺序无规律(添加的方法少的时候大多是按添加顺序的反顺序执行的,但是添加的多了就无规律了),所以添加多个的时候,不依赖执行顺序的还好,若是依赖于函数执行顺序,最好自己处理.

题目3: 解释IE事件冒泡和DOM2事件传播机制?

  • IE的事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的元素

事件冒泡

  • DOM2级事件规定事件流包括三个阶段,事件捕获阶段,处于目标阶段,事件冒泡阶段

DOM事件流

题目4:如何阻止事件冒泡? 如何阻止默认事件?

  • 阻止事件冒泡 :stopPropagation()
  • 阻止默认事件:preventDefault()

题目5:有如下代码,要求当点击每一个元素li时控制台展示该元素的文本内容,不考虑兼容

<ul class="ct">
    <li>这里是</li>
    <li>饥人谷</li>
    <li>前端6班</li>
</ul>
<script>
  var ct = document.querySelector('.ct');
   ct.addEventListener('click', function(a) {
       console.log(a.target.innerText);
      })
</script>

题目6: 补全代码,要求:

当点击按钮开头添加时在<li>这里是</li>元素前添加一个新元素,内容为用户输入的非空字符串;当点击结尾添加时在最后一个 li 元素后添加用户输入的非空字符串.
当点击每一个元素li时控制台展示该元素的文本内容。


<ul class="ct">
    <li>这里是</li>
    <li>饥人谷</li>
    <li>任务班</li>
</ul>
<input class="ipt-add-content" placeholder="添加内容" />
<button class="btn-add-start">开头添加</button>
<button class="btn-add-end">结尾添加</button>

<script>
    var ct = document.querySelector('.ct');
    ct.addEventListener('click', function (e) {
        if (e.target.tagName.toLowerCase() === 'li') {
            console.log(e.target.innerText);
        }
    });

    //开始添加
    //var start = document.getElementById('btn-add-start');
    var start = document.querySelector('.btn-add-start');
    start.addEventListener('click', function () {
        var li = document.createElement('li');
        var text1 = document.createTextNode(content.value);
        li.appendChild(text1);
        ct.insertBefore(li, ct.childNodes[0]);
    });

    //结尾添加
    var content = document.querySelector('.ipt-add-content');
    var addEndBtn = document.querySelector('.btn-add-end');
    addEndBtn.addEventListener('click', function () {
        var li2 = document.createElement('li');
        var text = document.createTextNode(content.value);
        li2.appendChild(text);
        ct.appendChild(li2);
    });
</script>

题目7: 补全代码,要求:当鼠标放置在li元素上,会在img-preview里展示当前li元素的data-img对应的图片。

<ul class="ct">
    <li data-img="http://cdn.jirengu.com/book.jirengu.com/img/11.jpg">鼠标放置查看图片1</li>
    <li data-img="http://cdn.jirengu.com/book.jirengu.com/img/11.jpg">鼠标放置查看图片1</li>
    <li data-img="http://cdn.jirengu.com/book.jirengu.com/img/11.jpg">鼠标放置查看图片1</li>
</ul>
<div class="img-preview">
  
</div>
<script>
  var ct = document.querySelector('.ct');
var preview = document.querySelector('.img-preview');
ct.addEventListener('mouseover',function(e){
  if(e.target.tagName.toLowerCase() === 'li'){
      var img = document.createElement('img');
      img.src = e.target.dataset.img;
     // console.log(e.target.dataset);
    //打印结果[object DOMStringMap] {img: "http://cdn.jirengu.com/book.jirengu.com/img/11.jpg"}
      preview.appendChild(img);
    
  }
  });
ct.addEventListener('mouseout', function(e){
  if(e.target.tagName.toLowerCase()==='li'){
    preview.innerHTML = '';
  }
})

</script>


题目8: 在 github 上创建个人项目,把视频里事件兼容的函数写法放入项目,在 Readme.md里描述项目

https://github.com/zhaipanyu/web-blog

进阶2-js数据类型运算符流程控制语句

进阶2

JavaScript 定义了几种数据类型? 哪些是原始类型?

  • 共有六种

数值(number):整数和小数(比如1和3.14)
字符串(string):字符组成的文本(比如"Hello World")
布尔值(boolean):true(真)和false(假)两个特定值
undefined:表示“未定义”或不存在,即此处目前没有任何值
null:表示空缺,即此处应该有一个值,但目前为空
对象(object):各种值组成的集合

  • 原始类型
    数值、字符串、布尔值,即基本数据类型

哪些是复杂类型?原始类型和复杂类型的区别是什么?

  • 对象是复杂类型,分成三个子类型
    狭义的对象(object)
    数组(array)
    函数(function)
    正则表达式 (regexp)
  • 区别

1 一个对象往往是多个原始类型的值的合成,可以看作是一个存放各种值的容器;
2 基本类型变量存的是值,复杂类型的变量存的是内存地址;
3 基本类型在赋值的时候拷贝值,复杂类型在赋值的时候只拷贝地址,不拷贝值;

typeof和instanceof的作用和区别?

  • typeof运算符 :可以返回一个值的数据类型.用来检测一个对象是否已经定义或者是否赋值.
    1)原始类型
    数值、字符串、布尔值分别返回number、string、boolean。
typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean" 

2)函数返回function。
3)undefined返回undefined。
4)除此以外,其他情况都返回object。

  • instanceof
    instanceof主要的目的是检测引用类型,判断对象是Array,还是RegExp
var array=new Array();  
var object=new Object();  
var regexp=new RegExp();  
function func(){};  
var func1=new func();  
  
alert(array instanceof Array);  //true  
alert(object instanceof Object);  //true  
alert(regexp instanceof RegExp);  //true  
alert(func1 instanceof func);  //true  
  • 区别
    ypeof和instanceof的目的都是检测变量的类型,两个的区别在于typeof一般是检测的是基本数据类型,instanceof主要检测的是引用类型!

如何判断一个变量是否是数字、字符串、布尔、函数

typeof 1 // "number"
typeof "asd" // "string"
typeof true // "boolean"
typeof function{alert("asd");} // "function"

NaN是什么? 有什么特别之处?

  • NaN(Not a number),表示非数字,NaN和任何值都不相等,自己不等于自己:
    NaN == NaN //false

如何把非数值转化为数值?

  • parseInt()将参数转化为整数
    parseFloat()将参数转化成浮点数
    number()将参数转换成数字类型

==与===有什么区别

  • ===不会自动转换数据类型,只当数据类型和值两者都一致才返回ture;NaN和自身不相等.
    而==会自动转换数据类型再比较,若值相等则返回ture.

break与continue有什么区别

  • break 用于强制退出循环体,执行循环后面的语句
  • continue 用于退出本次循环,执行下次循环
for(var i = 1; i< 10; i++){
    if(i % 4 === 0){
        break;    //若执行到这一步,就终止外面的for循环体.
    }

    console.log(i);
}

for(var i = 1; i< 10; i++){
    if(i % 4 === 0){
        continue;     //若执行到这一步,就跳过 i = 4的打印结果.然后继续i = 5的计算.
    }

    console.log(i);
}

void 0 和 undefined在使用场景上有什么区别

  • void 0:它是一个一元操作符,它的唯一作用就是返回一个undefined,不管这个操作符后面传的操作数是什么
    1.点击不做跳转
<a href="javascript:void(0);">
  Click here to do nothing
</a>

2.用于闭包避免解析错误

void function fn(){
  console.log("I will show immediately.")
}()
为什么不直接使用undefined这个值的字面量形式呢?
:因为undefined
既不是保留字,也不是关键字,它可以作为变量标识符赋值,所以你手写出来的undefined是有可能被覆盖的
  • undefined
    (1)变量被声明了,但没有赋值时,就等于undefined。
    (2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
    (3)对象没有赋值的属性,该属性的值为undefined。
    (4)函数没有返回值时,默认返回undefined。
var i;
i // undefined

function f(x){console.log(x)}
f() // undefined

var  o = new Object();
o.p // undefined

var x = f();
x // undefined

以下代码的输出结果是?为什么?

console.log(1+1); // 2
原因: 都是数值类型相加.1+1=2.
console.log("2"+"4");//  "24"
: 字符串拼接.

console.log(2+"4"); // "24"
: 一个数值与一个字符串相加时,会把其中的数值转为字符串,结果就是2个字符串拼接.
console.log(+"4");// 4
:+会将字符串转换为数字.

以下代码的输出结果是?

var a = 1;  
a+++a;  
typeof a+2;//"number2"

因为:
var a = 1;  
(a++)+a;  //1+1=2
(typeof a)+2; //"number"+2="number2"

以下代码的输出结果是? 为什么

 var a = 1;
 var b = 3;
 console.log( a+++b );//4
: ( a+++b )= (a++)+b;
而(a++)+b=1+3=4;


遍历数组,把数组里的打印数组每一项的平方

var arr = [3,4,5]

var arr = [3,4,5];
for(i=0;i<arr.length;i++){console.log((arr[i])*(arr[i])); }

遍历 JSON, 打印里面的值

var obj = {
 name: 'hunger', 
 sex: 'male', 
 age: 28 
}

答:

var obj = {
 name: 'hunger', 
 sex: 'male', 
 age: 28 
}
for(i in obj){ console.log(i+":"+ obj[i])}

以下代码输出结果是? 为什么 (选做题目)

var a = 1, b = 2, c = 3;
var val = typeof a + b || c >0
console.log(val) //number2
: 因为typeof的优先级较高所以typeof a 为number,此后又与b拼接为"number2",对于||运算符,两边都为true则返回前者

var d = 5;
var data = d ==5 && console.log('bb')
console.log(data)//"bb"
:因为&&左边为true, 
而对于 expr1 && expr2,如果expr1 能转换成false则返回expr1,否则返回expr2.
var data2 = d = 0 || console.log('haha')
console.log(data2)//"haha"
:  ||的左边=0,即false
对于expr1 || expr2,如果expr1能转换成true则返回expr1,否则返回expr2
var x = !!"Hello" + (!"world", !!"from here!!");
console.log(x)//2
:右边为 ture+ (false,true). = 1+1=2


参考

运算符:
Paste_Image.png

常见设计模式

为什么有设计模式的概念?
1.设计模式是代码设计经验的总结,为了可重用代码,保证代码的可靠性等.
2.设计模式主要分为三大类型:创建型模式,结构型模式和行为型模式.

1.单例模式

  • 定义:
  • 单件模式确保一个类只有一个实例,并提供一个全局访问点.
  • 使用场景:
  • 用于创建独一无二的,只能有一个实例的对象,单件模式给了我们一个全局的访问点,和全局变量一样方便又没有全局变量的缺点.
  • 把全局变量当成单例来使用容易造成命名污染.
    防止命名空间污染的方法:
  • 使用命名空间
  • 使用闭包封装私有变量
<script type="text/javascript">
    var People = (function () {//var的函数方法所以用大写
        var instance;
        function init() {
            //定义私有方法和属性
            //做某事
            return {
            //定义公共方法和属性
            };
        }
        return {
            createPeople: function () {
                if (!instance) {//只能有一份内存的对象,有就不创建,没就创建
                    instance = init();
                }
                return instance;
            }
        };
    }());
    var obj1 = People.createPeople();
    var obj2 = People.createPeople();

</script>

2.构造函数模式

  • 组件,封装,复杂.
  • JavaScript里函数有个原型属性叫prototype,当调用构造函数创建对象的时候,所有该构造函数原型的属性在新创建对象上都可用
  • 构造函数用于创建特定类型的对象,不仅声明了使用的对象,构造函数还可以接受参数。
  • 你可以自定义自己的构造函数,然后在里面声明自定义类型对象的属性或方法.
<script type="text/javascript">
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    Person.prototype.sayName = function () {
        return this.name;
    };
    var student = new Person('tony', 20);
    console.log(student);

</script>

3.混合模式

  • 为什么使用混合模式实现继承?
    实现对象的继承,我们可以通过对象冒充,也可以通过原型链的方式.
    但是,对象冒充就要求必须使用构造函数方式,而原型链则无法使用构造函数,那么,我们就综合一下,采区混合模式来实现继承.
  • 创建类的最好方式,是用构造函数定义属性,使用原型方式定义方法.这样的机制同样适用于继承机制,用对象冒充来继承构造函数的属性,用原型链继承prototype对象的方法
<script type="text/javascript">
    var Person = function (name, age) {
        this.name = name;
        this.age = age;
    };
    Person.prototype.sayName = function () {
        console.log(this.name);
    }
    var Student = function (name, age, score) {
        //这里的 call作用:改变作用域,可以引用构造函数
        Person.call(this, name, age);//this是student
        this.score = score;
        //student继承了person的属性
    };
    //Object.create()可以调用这个方法来创建一个新对象。
    //新对象的原型就是调用 create方法时传入的第一个参数
    Student.prototype = Object.create(Person.prototype);
    //student继承了person的方法
    // Student.prototype = create(Person.prototype);
    // function create (parentObj){
    //     function F(){}
    //     F.prototype = parentObj;
    //     return new F();
    // };//这一段等同于上面Object.create.(Person.prototype).
    Student.prototype.sayScore = function () {
        console.log(this.score);
    }
    var student = new Student("likefool", 18, 90);
    console.log(student);//obj{属性+方法}
    student.sayName();//'likefool'
//student继承了person的属性和方法
//混合模式= 构造函数模式 + call继承属性

</script>

4.工厂模式

  • 使用场景
  • 创建新对象,且该对象需要被被封装.
    工厂模式通过让子类来决定该创建的对象是什么,来达到将对象创建的过程封装的目的.
    创建对象的方法使用的是继承,用于创建一个产品的实例.
<script type="text/javascript">
    function createPerson(opts) {
        var person = {
            name: opys.name || 'peter'
        };
        person.sayName = function () {
            console.log(this.name);
        }
        return person;
    }
    var p1 = createPerson({ name: 'tom' });
    var p2 = createPerson({ name: 'kite' })

</script>

5.模块模式

  • 立即执行函数,直接return结果供外部使用,不污染全局变量
<script type="text/javascript">
var Person = (function(){
	var name = 'ruoyu';
	function sayName(){
		console.log(name);
	}
	
	return {
		name: name,
		sayName: sayName
	}
})()
Person.sayName();
</script>

6.发布订阅模式(即观察者模式)

  • 观察者模式又叫发布订阅模式,它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。
  • 使用观察者模式的好处:
    支持简单的广播通信,自动通知所有已经订阅过的对象。
    页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。
  • 观察者主要让订阅者与发布者解耦,发布者不需要知道哪些模块订阅了这个主题,它只管发布这个主题就可以了,同样订阅者也无需知道那个模块会发布这个主题,它只管订阅这个主题就可以了
<script type="text/javascript">
    var EventCenter = (function () {
        var events = {};
        /*
          {
            my_event: [{handler: function(data){xxx}}, {handler: function(data){yyy}}]
          }
        */
        //上面用数组保存方法的原因:这样实现了一个fire调用多个方法.一对多.
        function on(evt, handler) {
            events[evt] = events[evt] || [];
            events[evt].push({
                handler: handler
            });
        }
        function fire(evt, args) {
            if (!events[evt]) {
                return;
            }
            for (var i = 0; i < events[evt].length; i++) {
                events[evt][i].handler(args);
                
            }
        }
        return {
            on: on,
            fire: fire
        }
    })();

    EventCenter.on('my_event', function (data) {
        console.log('my_event received...');
    });
    EventCenter.on('my_event', function (data) {
        console.log('my_event2 received...');
    });
    EventCenter.fire('my_event',);
//逻辑:
//最外面放一个空对象.
//A方法作用:讲若干函数放入数组
//B方法:调用A里面所有的方法.
//AB怎么联系起来:通过外面的events对象,AB都是操作events对象.
//所以1对多.
</script>

7.发布订阅模式的范例

<script type="text/javascript">
    var EventCenter = (function () {
        //外部创建一个可以包含数组的对象
        var events = {};
        /*
          {
            my_event: [{handler: function(data){xxx}}, {handler: function(data){yyy}}]
          }
        */
        //这里只保存方法到数组,不做操作.
        function on(evt, handler) {
            events[evt] = events[evt] || [];
            events[evt].push({
                handler: handler
            });
        }
        //对上面保存在数组里的函数做相关操作
        //重点是:都是对同一event对象的数组操作
        function fire(evt, args) {
            if (!events[evt]) {
                return;
            }
            for (var i = 0; i < events[evt].length; i++) {
                events[evt][i].handler(args);
            }
        }
        function off(evt) {
            delete events[evt]
        }
        return {
            on: on,
            fire: fire,
            off: off
        }
    })();
    EventCenter.on('my_event', function (data) {
        console.log('my_event received...');
    });
    EventCenter.on('my_event', function (data) {
        console.log('my_event2 received...');
    });
    EventCenter.fire('my_event');
    EventCenter.on('change', function (val) {
        console.log('change...  now val is ' + val);
    });
    EventCenter.fire('change', 'Tom');
    EventCenter.off('change');//events[change]就被删除了
    //所以不会再调用change相关方法(通知),delete原理就是删除数组里面的元素(即方法)
   </script>

(Math+Aarry+Date) demo

Math任务

1、写一个函数,返回从min到max之间的 随机整数,包括min不包括max

function rand(min,max){
  var a = Math.floor(Math.random()*(max - min) +min);
  console.log(a);
}
rand(0,10);
//Math.random()函数返回0和1之间的伪随机数,可能为0,但总是小于1,[0,1)
//floor方法返回小于参数值的最大整数

2、写一个函数,返回从min都max之间的 随机整数,包括min包括max

function rand(min,max){
  var a = Math.round(Math.random()*(max - min) +min);
  console.log(a);
}
rand(0,10);
//round(x):把数四舍五入为最接近的整数

3、写一个函数,生成一个长度为 n 的随机字符串,字符串字符的取值范围包括0到9,a到 z,A到Z。

function getRandStr(len){
  var str1 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  var result = '';
  for(var i = 0;i<len; i++){

    result += str1[Math.floor(Math.random() * (str1.length))];//数组下标从0开始,所以选floor
  }
  return result;
  
 
}

var str =  getRandStr(10);
console.log(str);
//思路:把所有目标0~z的集合用数组的下标取.

4、写一个函数,生成一个随机 IP 地址,一个合法的 IP 地址为 0.0.0.0~255.255.255.255

function randIp(){
  var arr = [];
  for(var i = 0; i<4; i++){
    arr[i] = Math.floor(Math.random() *256);//易忘arr的[i],不是+= 
    
  }
  return arr.join('.');
  
}

 var str = randIp();
 console.log(str);

5、写一个函数,生成一个随机颜色字符串,合法的颜色为#000000~ #ffffff

function getRandColor(){
  var str = '0123456789abcdef'; //16位 
  var result = '';
  for(var i = 0; i < 6; i++ ){
    result += str[ Math.floor(Math.random() * (str.length))];
    
  }
  return ('#' +  result);
    
}
var color = getRandColor();
console.log(color);   // #3e2f1b
//孰能生巧,第一题会了其他题套路一样

数组任务

1、数组方法里push、pop、shift、unshift、join、splice分别是什么作用?用 splice函数分别实现push、pop、shift、unshift方法

  • push:将一个或多个元素添加到数组的末尾并返回数组的新长度。
//splice函数实现push
var arr = ['a','b','c','d'];
arr.splice(arr.length, 0, 'ha');
console.log(arr); //["a", "b", "c", "d", "ha"]

  • pop:从数组中删除最后一个元素并返回该元素,此方法更改数组的长度
//splice函数实现pop
var arr2 = ['a','b','c','d'];
arr2.splice(arr2.length-1, 1);
console.log(arr2); //["a", "b", "c"]
  • shift:从数组中删除第一个元素并返回该元素,此方法更改数组的长度。

//splice函数实现shift
var arr3 = ['a','b','c','d'];
arr3.splice(0, 1);
console.log(arr3); //["b", "c", "d"]
  • unshift:将一个或多个元素添加到数组的开头,并返回新数组的新长度。
//splice函数实现unshift
var arr = ['a','b','c','d'];
arr.splice(0, 1,'ha','he');
console.log(arr);  //["ha", "he", "b", "c", "d"]
  • join:该方法将数组(或数组类对象的)所有元素连接到字符串中,返回连接了所有数组元素的字符串.

  • splice:该方法通过删除现有元素和/或添加新元素来更改数组的内容.
    还可参考我这篇简书

2、写一个函数,操作数组,数组中的每一项变为原来的平方,在原数组上操作

function squareArr(arr){
  for(var i = 0; i<arr.length; i++){
    arr[i] = arr[i] * arr[i];
  }
  return arr;
}
var arr = [2, 4, 6];
squareArr(arr);
console.log(arr); // [4, 16, 36]

3、写一个函数,操作数组,返回一个新数组,新数组中只包含正数,原数组不变

function filterPositive(arr){
  var arr1 = [];
  for(var i = 0; i<arr.length;i++){
    if(arr[i]>0  && typeof(arr[i]) === 'number'){
      arr1.push(arr[i]);
    }
  }
  return arr1;
}
var arr = [3, -1,  2,  '饥人谷', true];
var newArr = filterPositive(arr);
console.log(newArr); //[3, 2]
console.log(arr); //[3, -1,  2,  '饥人谷', true]

Date 任务

1、 写一个函数getChIntv,获取从当前时间到指定日期的间隔时间

法1:好理解
function fn(Datestr){
  var now = new Date();
  var end = new Date(Datestr);
  var times = (end.getTime() - now.getTime())/1000;//总秒数
  var day = parseInt(times/3600/24);//总天数
  var hour = parseInt( (times - day*3600*24)/3600);//小时
  var minute = parseInt((times - day*24*3600 - hour*3600)/60);
  var second = parseInt(times - day*24*3600 - hour*3600 - minute*60);
  
  var time = '距离白富美回我微信还有:' + day + '天' + hour + '小时' + minute + '分'  + second + '秒';
  console.log(time);
}

fn('2017/11/11');
法2:
function getChIntv(t_date){
    var now = new Date();
    var end = new Date(t_date);
    var times = (end.getTime() - now.getTime())/1000;//秒
    var d = parseInt(times/ (60*60*24));//天
    var h = parseInt(times % (60*60*24)/(60*60));//
    var m = parseInt(times % (60*60)/(60));
    var s = parseInt(times % (60)/(1000));
    return  '距离女神洗完澡还有:' + d + "天" + h +"小时" + m + "分" + s + "秒";

}
var str = getChIntv("2017-10-01");
console.log(str);

date笔记:

parseInt() 函数可解析一个字符串,并返回一个整数
getTime() :返回 Date 对象与'1970/01/01 00:00:00'之间的毫秒值(北京时间的时区为东8区,起点时间实际为:'1970/01/01 08:00:00') 。

getDate()从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getDay()从 Date 对象返回一周中的某一天 (0 ~ 6)。

创建 Date 对象的语法:
var myDate=new Date()
注释:Date 对象会自动把当前日期和时间保存为其初始值。

返回值:

{Date} 返回一个转换后的 Date 对象。

示例:

[js] view plaincopy
var dt = new Date('2014/12/25'); // yyyy/MM/dd  

console.log(dt); // => {Date}:2014/12/25 00:00:00  

dt = new Date('2014/12/25 12:00:00'); // yyyy/MM/dd HH:mm:ss  

console.log(dt); // => {Date}:2014/12/25 12:00:00  

dt = new Date('2014-12-25'); // yyyy-MM-dd  

console.log(dt); // => {Date}:2014-12-25 08:00:00 (加上了东8区的时区)  

dt = new Date('2014-12-25 12:00:00'); // yyyy-MM-dd HH:mm:ss (注意:此转换方式在IE中会报错!)  

console.log(dt); // => {Date}:2014-12-25 12:00:00 

参考Date
参考极客data

2、把hh-mm-dd格式数字日期改成中文日期

//把hh-mm-dd格式数字日期改成中文日期
function getChsDate(date){
  var arr = ["零","一","二","三","四","五","六","七","八","九","十",
           "十一","十二","十三","十四","十五","十六","十七","十八","十九","二十",
           "二十一","二十二","二十三","二十四","二十五","二十六","二十七","二十八",
           "二十九","三十","三十一"];
  
  var arr1 = date.split('-');//['2015','01','08']
  var year = '',month =  '',day = '';//分为三部分
  for(var i in  arr1[0]){
    year += arr[arr1[0][i]];
  }
  month = arr[parseInt(arr1[1])];
  day = arr[parseInt(arr1[2])];
  return year +'年' +month +'月' +day +'日';
 
  
}
var str = getChsDate('2015-11-28');
console.log(str);  // "二零一五年十一月二十八日"

3、写一个函数,参数为时间对象毫秒数的字符串格式,返回值为字符串。假设参数为时间对象毫秒数t,根据t的时间分别返回如下字符串:

刚刚( t 距当前时间不到1分钟时间间隔)
3分钟前 (t距当前时间大于等于1分钟,小于1小时)
8小时前 (t 距离当前时间大于等于1小时,小于24小时)
3天前 (t 距离当前时间大于等于24小时,小于30天)
2个月前 (t 距离当前时间大于等于30天小于12个月)
8年前 (t 距离当前时间大于等于12个月)

<script>
    function friendlyDate(time) {

        var now = Date.now();//当前时间
        //   console.log('now: ' + now);
        //   console.log('time: ' + time);
        var timeDiffer = (now - time) / 1000;//相减
        //   console.log(timeDiffer);

        if (timeDiffer < 60) {

            console.log('刚刚');
        } else if (timeDiffer < 60 * 60) {

            console.log('3分钟前');
        } else if (timeDiffer < 24 * 60 * 60) {

            console.log('8小时前');
        }
        else if (timeDiffer < 30 * 24 * 60 * 60) {

            console.log('3天前');
        }
        else if (timeDiffer < 12 * 30 * 24 * 60 * 60) {

            console.log('2个月前');
        } else {

            console.log('8年前');
        }

    }
    var str = friendlyDate('1493287221905');
    // console.log(str);//  1分钟前
    var str2 = friendlyDate('1483941245793'); //4天前

</script>

js-引用类型对象拷贝

进阶4

1.引用类型有哪些?非引用类型有哪些

  • 引用类型:
    引用类型(Object, Array, Function, Date, Math, RegExp, Error)指的是那些保存在堆内存中的对象,变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,由该位置保存对象。

  • 非引用类型(基本数据类型):
    number,string,boolean,undefined, null.
    指的是保存在栈内存中的简单数据段.

2.如下代码输出什么?为什么

var obj1 = {a:1, b:2};
var obj2 = {a:1, b:2};
console.log(obj1 == obj2);//输出:fasle:会比较两者内存地址,发现内存地址不一样.

console.log(obj1 = obj2);// 输出 : Object {a: 1,b: 2}
:这一步把obj2的内存地址给了obj1,从此两者共用一份内存地址.

console.log(obj1 == obj2);//输出:ture :因为两者共用一份内存地址了

3.如下代码输出什么? 为什么

var a = 1;
var b = 2;
var c = { name: '饥人谷', age: 2 };
var d = [a, b, c];

var aa = a;//a=1
var bb = b;//a=2
var cc = c;
var dd = d;

a = 11;//aa=1:基本类型引用,只会让a=11,aa和a 是指向两块不同的内存空间(即指针).不干扰
b = 22;//bb=2
c.name = 'hello';//{name: "hello", age: 2}
d[2]['age'] = 3;//这句把c对象字面量改为{name: "hello", age: 3}

console.log(aa); //1 
console.log(bb);//2
console.log(cc);//Object{name: "hello", age: 3}  :引用类型中:两个变量指向同一个堆对象,改变其中一个变量,另一个也会受到影响
console.log(dd);//[1,2,Object{age: 3, name: "hello"}]



4.如下代码输出什么? 为什么

var a = 1;
var c = { name: 'jirengu', age: 2 };

function f1(n){
  //var n =a;相当于有这步,但并没有给a开辟新的内存.
  ++n;
}
function f2(obj){
  ++obj.age;
}

f1(a);//a=1  n=2
f2(c);//c={name: 'jirengu', age: 2 };
f1(c.age);//c={name: 'jirengu', age: 3 };对象a的点语法访问的是同一个内存地址,所以原指针c的age属性会改变.
console.log(a);//a=1
console.log(c);//Object {name: 'jirengu', age: 3 };

5.过滤如下数组,只保留正数,直接在原数组上操作

 var arr = [3,1,0,-1,-3,2,-5];
  
  function filter(arr){

    for(var i=0;i<arr.length;i++){
      if( arr[i]<1 ) {
        arr.splice(i,1); //splice(i,1)会从第i个开始,删除1个数,用于数组增删元素
        --i;  //当删除一个元素,arr的长度会减1,但for循环的i会一直加1,这样每次删一个,会漏掉被删元素后一个的元素的判断
      }
 
    }

  }
  filter(arr);
  console.log(arr); // [3,1,2]

6.过滤如下数组,只保留正数,原数组不变,生成新数组

方法1:

 var arr = [3,1,0,-1,-3,2,-5];
    function filter(arr){
      var newarr = [];
      for(var i = 0;i<arr.length;i++ ){
         if(arr[i]>0 ){
           newarr.splice(i,0,arr[i]);//splice第二个参数如果设置为 0,则不会删除项目,第三个参数是添加的参数.
         }
      }
      return newarr;
    }
    var arr2 = filter(arr);
    console.log(arr2); // [3,1,2]
    console.log(arr);  // [3,1,0,-1,-2,2,-5]

方法2:

var arr = [3,1,0,-1,-3,2,-5];
function filter(arr){
    var newArr = [];
    for(var i = 0; i<arr.length;i++){
        if(arr[i]>0){
            newArr.push(arr[i]);
        }
    }
    return newArr;
}
var arr1 = filter(arr);
console.log(arr1);
console.log(arr);

方法3:

var arr = [3,1,0,-1,-3,2,-5];
function filter(arr){
  var j = 0;
  var newarr = [];
  for( var i = 0; i < arr.length; i++){
    if( arr[i] > 0){
      newarr[j] = arr[i];
      j++;
    }
  }
  return newarr;
}
var arr2 = filter(arr);
console.log(arr2); // [3,1,2]
console.log(arr);  // [3,1,0,-1,-2,2,-5]

7.写一个深拷贝函数,用两种方式实现

  • 在JavaScript中,对于Object和Array这类引用类型值,当从一个变量向另一个变量复制引用类型值时,这个值的副本其实是一个指针,两个变量指向同一个堆对象,改变其中一个变量,另一个也会受到影响

  • 而引用类型拷贝分为两种
    浅拷贝:拷贝原对象的引用.
    深拷贝:是拷贝出一个新的实例,新的实例和之前的实例互不影响.两份不同的内存地址.

  • 方法一

function deepCope(obj){
    var newObj = {}; 
    for(var key in obj){
     //只赋值对象自己的属性,原型上的属性不赋值
      if(obj.hasOwnProperty(key)){//obj.hasOwnProperty(prop)这个方法可以用来检测一个对象是否含有特定的自身属性
        if(typeof obj[key] ==== 'number' ||
           typeof obj[key] ==== 'string' ||
           typeof obj[key] ==== 'undefined' ||
           typeof obj[key] ==== 'boolean' ||
                  obj[key] ==== null){//由于typeof null返回值也是object,所以不能直接用typeof obj[key] === 'object'来判断
         newObj[key] = obj[key];
        }else{
          newObj[key] = deepCope(obj[key]);//递归
      }
        
    }
      return newObj;
      
  }



  • 上述的hasOwnProperty 方法判断属性是否存在,下面的例子检测了对象 o 是否含有自身属性 prop
o = new Object();
o.prop = 'exists';

function changeO() {
  o.newprop = o.prop;
  delete o.prop;
}

o.hasOwnProperty('prop');   // 返回 true
changeO();
o.hasOwnProperty('prop');   // 返回 false
  • 方法二
下面两个方法可以实现对象的深复制.
JOSN对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以把JSON字符串反序列化为一个js对象
// 利用JSON序列化实现一个深拷贝
function deepClone(source){
  return JSON.parse(JSON.stringify(source));
}
var o1 = {
  arr: [1, 2, 3],
  obj: {
    key: 'value'
  },
  func: function(){
    return 1;
  }
};
var o2 = deepClone(o1);
console.log(o2); // => {arr: [1,2,3], obj: {key: 'value'}}

参考

OOP_原型_木桶布局

1. OOP 指什么?有哪些特性

  • OOP指的是面向对象编程

  • 三大特性:封装、继承、多态
    1.封装
    封装指的是将方法和属性保存起来,防止外界直接调用的一种手段;若要访问属性和方法,必须先声明一个类的实例,通过这个实例的接口去访问属性和调用方法
    2.继承
    A对象有一些属性和方法,B对象有一些属性和方法;若A对象继承自B对象,A对象可以访问B对象的属性和方法
    3.多态
    当两个对象继承自同一个类时,每个对象对父类的某一方法或者属性重写后的不同表现形式就是多态;
    例子:鸡类和狗类都继承自动物类;动物类有say()方法,鸡类和狗类继承后分别重写为function say(){console.log('鸡叫')}和function say(){console.log('狗叫')}

2. 如何通过构造函数的方式创建一个拥有属性和方法的对象?

        //构造函数第一个字母大写
       var Human = function () { this.name = 'human' }
        Human.prototype.eat = function () { console.log('吃饭') }
        var man = new Human;
        console.dir(man)

image.png

3. prototype 是什么?有什么特性

  • 每一个函数在创建之后都会拥有 prototype 的属性,这个属性指向函数的原型对象
    原型对象可视为公共区域,默认含有 constructor、 proto 这两个属性
    使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法,通常我们可以将实例对象的公共属性和方法放在 prototype 对象中

  • 特性:
    用来实现基于原型的继承与属性的共享
    所有对象都有 proto,指向其构造函数的prototype
    obj.proto === Object.prototype //true
    构成原型链,同样用于实现基于原型的继承,例:访问一个对象的属性时,如果在obj中找不到,那么就会沿着__proto__依次查找,直到找到这个属性或null

4.画出如下代码的原型图

function People (name){
  this.name = name;
  this.sayName = function(){
    console.log('my name is:' + this.name);
  }
}

People.prototype.walk = function(){
  console.log(this.name + ' is walking');  
}

var p1 = new People('后端');
var p2 = new People('前端');

image.png

5. 创建一个 Car 对象,拥有属性name、color、status;拥有方法run,stop,getStatus

//创建一个 Car 对象,拥有属性name、color、status;拥有方法run,stop,getStatus
var Car = function (name, color, status) {
    this.name = name
    this.color = color
    this.status = status

}
Car.prototype.run = function () {
    console.log('----->run')
}
Car.prototype.stop = function () {
    console.log('----->stop')
}

Car.prototype.getStatus = function () {
    console.log('----->getStatus:'+this.status)
}

var car = new Car('哈哈','pink',true);
car.run();//----->run
car.stop();
car.getStatus(); 

6. 创建一个 GoTop 对象,当 new 一个 GotTop 对象则会在页面上创建一个回到顶部的元素,点击页面滚动到顶部。拥有以下属性和方法

  1. ct属性,GoTop 对应的 DOM 元素的容器
  2. target属性, GoTop 对应的 DOM 元素
  3. bindEvent 方法, 用于绑定事件
    4 createNode 方法, 用于在容器内创建节点

预览

7. 使用木桶布局实现一个图片墙

预览
代码

8.拓展阅读

JS 的 new 到底是干什么的?
JS 原型链

js__JSONP__跨域

1: 什么是同源策略

  • 最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页**"同源"**,所谓"同源"指的是"三个相同".
    协议相同
    域名相同
    端口相同

  • 举例来说:
    http://www.example.com/dir/page.html
    这个网址协议是http://,域名是www.example.com,端口是80(默认端口可以省略),它的同源情况如下.
    http://www.example.com/dir2/other.html:同源
    http://example.com/dir/other.html:不同源(域名不同)
    http://v2.www.example.com/dir/other.html:不同源(域名不同)
    http://www.example.com:81/dir/other.html:不同源(端口不同)
    https://www.example.com:81/dir/other.html:不同源(https协议不同)

2: 什么是跨域?跨域有几种实现形式

  • 跨域出现的原因
    JavaScript出于安全方面的考虑,不允许一个网页访问一个非同源的网页,即2个网址的协议相同,域名相同,端口相同其中任意一个不同就是非同源.
  • 概念:只要协议、域名、端口有任何一个不同,都被当作是不同的域。
    比如:有一个Ajax的var xhr=new XMLHttpRequest()的xhr对象,在a网址里发生请求到一个非同源的b网址,会请求的报错.

不跨域会出现图片的的报错

  • 跨域就是为了解决这个问题,实现非同源网页之间的数据传输和通信.

跨域常见方式

  • JSONP(JSON with Padding 填充式JSON 或参数式JSON)

  • CORS(Cross-Origin Resource Sharing,跨源资源共享)

  • HTML5的window.postMessage
    window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
    window.postMessage允许两个窗口/帧之间跨域发送数据消息。从本质上讲,window.postMessage是一个跨域的无服务器垫片的Ajax。

  • 降域: document.domain
    使用条件
    有其他页面 window 对象的引用,
    二级域名相同,
    协议相同,
    端口相同

//在页面 http://www.example.com/a.html 中设置document.domain:
<iframe src="example.com/b.html" id="iframe" onload="test()"></iframe>
    <script>
        document.domain='example.com';//设置成主域
        function test(){
            alert(document.getElementById('iframe').contentWindow);
        }
    </script>
//在页面 http://example.com/b.html中也设置document.domain
<script>
        document.domain='example.com';//在iframe载入的这个页面也设置 document.domain与主页面相同
</script>
//而且是必须的,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值

3: JSONP 的原理是什么

  • JSONP (JSON with Padding)是一个简单高效的跨域方式,html中的script标签可以加载并执行其他域的JavaScript,于是我们可以通过script标记来动态加载其他域的资源,例如我要从域A的页面pageA加载域B的数据,那么在域B的页面pageB中我以JavaScript的形式声明pageA需要的数据,然后在pageA中用script标签把pageB加载进来,那么pageB中的脚本就会得以执行。JSONP在此基础上加入了回调函数,pageB加载完之后会执行pageA中定义的函数,所需要的数据会以参数的形式传递给该函数。JSONP易于实现,但是也会存在一些安全隐患,如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据。但是在受信任的双方传递数据,JSONP是非常合适的选择。

  • .css,.js,图片的引用和jsonp跨域拿到js方法有什么区别 ?
    相同点:
    在html里.js,图片的引用和jsonp拿到js的方法是一样的,都是从服务器那到js文件然后插入到html里.
    不同:
    只不过出发点是不一样,前者是加载资源,后者为了跨域拿后台返回的js.
    jsonp还多了一个回调,区别是出发点和应用方法不同,但都有从服务器返回的资源.

4: CORS是什么

  • CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通.CORS背后的基本**就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败.跨域后浏览器不会返回数据.

  • **简单请求 **

浏览器将CORS请求分成两类:简单请求(simple request)和
简单请求条件
1) 请求方法是以下三种方法中的一个:
HEAD
GET
POST
2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
凡是不同时满足上面两个条件,就属于非简单请求

  • 使用
    在服务器后台设置header属性Access-Control-Allow-Origin
    它的值是请求时Origin字段的值或者 * , * 表示接受任意域名的请求。
app.get('/getNews', function(req, res){

	var news = [
		"第11日前瞻:**冲击4金 博尔特再战200米羽球",
	]
	var data = [];
	for(var i=0; i<3; i++){
		var index = parseInt(Math.random()*news.length);
		data.push(news[index]);
		news.splice(index, 1);
	}
	res.header("Access-Control-Allow-Origin", "http://a.jrg.com:8080"); //代表只接受http://a.jrg.com:8080网址的请求
	//res.header("Access-Control-Allow-Origin", "*"); //*表示接受任意域名的请求

	res.send(data);
})
  • 非简单请求
    是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
    非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight).
    浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

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

跨域问题
跨域10种方式

5: 演示三种常用以上跨域的解决方式

  • jsonp

  • 先在客户端上设置 新 Hosts
    127.0.0.1 a.jrg.com
    127.0.0.1 b.jrg.com
    127.0.0.1 jrg.com

router.js


app.get('/getNews', function(req, res){
var news = [
'我没有特别的才能,只有强烈的好奇心。永远保持好奇心的人是永远进步的人。——爱因斯坦',

'爱因斯坦认为他之所以取得成功,原因在于他具有狂热的好奇心.',
'求知欲,好奇心这是人的永恒的,不可改变的特性。哪里没有求知欲,哪里便没有学校。——苏霍姆林斯基',

'孩子提出的问题越多,那么他在童年早期认识周围的东西也就愈多,在学校中越聪明,眼睛愈明,记忆力愈敏锐。要培养自己孩子的智力,那你就得教给他思考。——苏霍姆林斯基',

'我想起了自己小学的学习经历,终于理解了为什么小时候成绩好,我那时候确实好奇心非常强烈.',
'人的内心里有一种根深蒂固的需要——总想感到自己是发现者、研究者、探寻者。在儿童的精神世界中,这种需求特别强烈。但如果不向这种需求提供养料,即不积极接触事实和现象,缺乏认识的乐趣,这种需求就会逐渐消失,求知兴趣也与之一道熄灭。(苏霍姆林斯基)',

'生活的全部意义在于无穷地探索尚未知道的东西,在于不断地增加更多的知识。——左拉'
	]
	var data = [];
	for(var i=0; i<3; i++){
		var index = parseInt(Math.random()*news.length);
		data.push(news[index]);
		news.splice(index, 1);//不重复上一步拿到的新闻
	}
	var cb = req.query.callback
	if(cb){
		res.send(cb + '('+ JSON.stringify(data) + ')');
	}else{
		res.send(data);
	}	
})

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>news</title>
<style>
  .container{
    width: 900px;
    margin: 0 auto;
  }
</style>
</head>
<body>
  <div class="container">
    <ul class="news">
      <li>我没有特别的才能,只有强烈的好奇心 ——爱因斯坦</li>
      <li>我没有特别的才能,只有强烈的好奇心 ——爱因斯坦</li> 
      <li>我没有特别的才能,只有强烈的好奇心 ——爱因斯坦</li>
    </ul>
    <button class="change">点我换一组</button>
  </div>
  
<script>
  
  $('.change').addEventListener('click', function(){
    var script = document.createElement('script');
    script.src = 'http://gaygay.com:8080/getNews?callback=appendHtml';//必须是'http://xxx.com:8080/的形式
   
    document.head.appendChild(script);
    document.head.removeChild(script);
  })
  
  function appendHtml(news){
    var html = '';
    for( var i=0; i<news.length; i++){
      html += '<li>' + news[i] + '</li>';
    }
    console.log(html);
    $('.news').innerHTML = html;
  }
  function $(id){//$函数,发请求前和点击换一组都调用.传入参数,直接返回.
    return document.querySelector(id);//替换$(id)为document.querySelector(id),因为浏览器不支持jquery库
  }
</script>

</body>

</html>

  • CORS

QQ20170512-220224-HD.gif

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>news</title>
<style>
 .container{
   width: 900px;
   margin: 0 auto;
 }
</style>
</head>
<body>
 <div class="container">
   <ul class="news">
     <li>CORS练习</li>
     <li>男双力争会师决赛 </li> 
     <li>女排将死磕巴西!</li>
   </ul>
   <button class="change">换一组</button>
 </div>
 
<script>
 
 $('.change').addEventListener('click', function(){
   var xhr = new XMLHttpRequest();
   xhr.open('get', 'http://b.jrg.com:8080/getNews', true);
   xhr.send();
   xhr.onreadystatechange = function(){
     if(xhr.readyState === 4 && xhr.status === 200){
       appendHtml( JSON.parse(xhr.responseText) )
     }
   }
   window.xhr = xhr//why?
 })
 function appendHtml(news){
   var html = '';
   for( var i=0; i<news.length; i++){
     html += '<li>' + news[i] + '</li>';
   }
   console.log(html);
   $('.news').innerHTML = html;
 }
 function $(id){
   return document.querySelector(id);
 }
</script>
</html>

router.js


app.get('/getNews', function(req, res){

	var news = [
		"第11日前瞻:**冲击4金 博尔特再战200米羽球",
		"正直播柴飚/洪炜出战 男双力争会师决赛",
		"女排将死磕巴西!郎平安排男陪练模仿对方核心",
		"没有**选手和巨星的110米栏 我们还看吗?",
		"中英上演奥运金牌大战",
		"博彩赔率挺**夺回第二纽约时报:**因对手服禁药而丢失的奖牌最多",
		"最“出柜”奥运?同性之爱闪耀里约",
		"下跪拜谢与洪荒之力一样 都是真情流露"
	]
	var data = [];
	for(var i=0; i<3; i++){
		var index = parseInt(Math.random()*news.length);
		data.push(news[index]);
		news.splice(index, 1);
	}
	res.header("Access-Control-Allow-Origin", "http://jrg.com:8080"); 
	//res.header("Access-Control-Allow-Origin", "*"); 
	res.send(data);

  • document.domain降域

QQ20170513-104421-HD.gif

情景:a.html里面嵌入iframe元素,且这个iframe是<iframe src="http://b.jrg.com:8080/b.html" frameborder="0" ></iframe>,而b.html就是个其中src规定显示在 iframe 中的文档的地址,也是绝对 URL - 指向其他站点(比如 src="www.example.com/index.html"),这里是个非同源的b.html

//a.html文件

<html>
<style>
  .ct{
    width: 910px;
    margin: auto;
  }
  .main{
    float: left;
    width: 450px;
    height: 300px;
    border: 1px solid #ccc;
  }
  .main input{
    margin: 20px;
    width: 200px;
  }
  .iframe{
    float: right;
  }
  iframe{
    width: 450px;
    height: 300px;
    border: 1px dashed #ccc;
  }
</style>

<div class="ct">
  <h1>使用降域实现跨域</h1>
  <div class="main">
    <input type="text" placeholder="http://a.jrg.com:8080/a.html">
  </div>

  <iframe src="http://b.jrg.com:8080/b.html" frameborder="0" ></iframe>

</div>


<script>
//URL: http://a.jrg.com:8080/a.html
document.querySelector('.main input').addEventListener('input', function(){
  console.log(this.value);
  window.frames[0].document.querySelector('input').value = this.value;
})

document.domain = "jrg.com"//降域关键代码
</script>
</html>


//b.html
<html>
<style>
	html,body{
		margin: 0;
	}
	input{
		margin: 20px;
		width: 200px;
	}
</style>

	<input id="input" type="text"  placeholder="http://b.jrg.com:8080/b.html">
<script>

 
document.querySelector('#input').addEventListener('input', function(){
	window.parent.document.querySelector('input').value = this.value;
})
document.domain = 'jrg.com';
</script>
</html>


  • window.frames[0].postMessage
//a.html文件

<html>
<style>
	.ct{
		width: 910px;
		margin: auto;
	}
	.main{
		float: left;
		width: 450px;
		height: 300px;
		border: 1px solid #ccc;
	}
	.main input{
		margin: 20px;
		width: 200px;
	}
	.iframe{
		float: right;
	}
	iframe{
		width: 450px;
		height: 300px;
		border: 1px dashed #ccc;
	}
</style>

<div class="ct">
	<h1>使用postMessage实现跨域</h1>
	<div class="main">
		<input type="text" placeholder="http://a.jrg.com:8080/a.html">
	</div>

	<iframe src="http://localhost:8080/b.html" frameborder="0" ></iframe>

</div>


<script>
//URL: http://a.jrg.com:8080/a.html
$('.main input').addEventListener('input', function(){
	console.log(this.value);
	window.frames[0].postMessage(this.value,'*');//window.frames[0]是window子窗口的第一个.*代表任意地址

})
window.addEventListener('message',function(e) {
		$('.main input').value = e.data
    console.log(e.data);
});

//
window.addEventListener('message',function(e){
  $('.mian input').value = e.data;
})


function $(id){
	return document.querySelector(id);
}
</script>
</html>
//b.html文件:即被内嵌的iframe链接文档url.
<html>
<style>
	html,body{
		margin: 0;
	}
	input{
		margin: 20px;
		width: 200px;
	}
</style>

	<input id="input" type="text"  placeholder="http://b.jrg.com:8080/b.html">
<script>

 
 //传出数据到内嵌此窗口的父窗口即a.html.
$('#input').addEventListener('input', function(){
	window.parent.postMessage(this.value, '*');//
	//返回当前窗口的父窗口对象.如果一个窗口没有父窗口,则它的 parent 属性为自身的引用.
    //如果当前窗口是一个 <iframe>, <object>, 或者 <frame>,则它的父窗口是嵌入它的那个窗口
})

//接收信息
window.addEventListener('message',function(e) {
		$('#input').value = e.data
    console.log(e.data);
});
function $(id){
	return document.querySelector(id);
}	
</script>
</html>

跨域实例的代码参考

js_jQuery选择器_Dom操作_样式_事件处理

为什么学习jQuery?
0.jQuery 由 John Resig 创建,是继 Prototype 之后又一个优秀的 Javascript 库,其宗旨是「写更少的代码,做更多的事情」
1.通过js原生语法如:document.getElementById等方法获取DOM对象,方法名称长,使用不方便.
2.jQuery定义了一套选择器规则,和CSS选择器目的一样,都是为了选择出符合特定规则的元素,代码简洁.
3.jQuery选择器方便使用者jQuery刻意和CSS选择器使用相同的语法,几乎支持所有类型的CSS3选择器

1: 说说库和框架的区别?

  • 库的英语为 Library,框架的英语为 Framework
    是将代码集合成的一个产品,供程序员调用。面向过程的代码组织形式而成的库也叫函数库。

  • 框架则是为解决一个(一类)问题而开发的产品,框架用户一般只需要使用框架提供的类或函数,即可实现全部功能。可以说,框架是库的升级版。
    开发者在使用框架的时候,必须使用这个框架的全部代码。

  • 库只管引进来使用,除了库接口,没有其他约束
    而框架则有着各种各样的严格约束。例如bootstrap,约束了DOM结构。例如AnglarJS,也对DOM结构有约束。这些都是框架。框架就像一门语言,有自己的世界。用了什么框架,就得按照这个框架世界里的规则行事编码

2: jquery 能做什么?

  • jQuery 是一个高效、精简并且功能丰富的 JavaScript 工具库。它提供的 API 易于使用且兼容众多浏览器,这让诸如 HTML 文档遍历和操作、事件处理、动画和 Ajax 操作更加简单

3: jquery 对象和 DOM 原生对象有什么区别?如何转化?

  • 区别
    jQuery对象就是通过jQuery包装DOM对象后产生的对象(集合对象)。jQuery对象是jQuery独有的,可以使用jQuery里的方法。
    因此jQuery对象和DOM对象是不一样的,不能调用对方定义的方法。
    dom对象须使用dom方法,jq对象需使用jq方法
    $(‘#test’).innerHTML会报错,document.getElementById(‘#test’)[0].html()也会报错。

  • 2者转换:

普通的DOM对象可以用$()包装起来转换为jQuery对象:$(document.getElementById(‘#test’)).html();//正常

jQuery对象本身是一个集合,要转换为DOM对象,可通过数组索引取出:
第一种方式:$(‘#test’)[0]
第二种方式:$(‘#test’).get(0)
注: eq(0)返回的还是jQuery对象,eq(0)[0]是DOM对象。

4:jquery中如何绑定事件?bind、unbind、delegate、live、on、off都有什么作用?推荐使用哪种?使用on绑定事件使用事件代理的写法?

  • 绑定事件
    on,bind,delegate,live,unbind,trigger

  • on
    自 jQuery 版本 1.7 起,on() 方法是 bind()、live() 和 delegate() 方法的新的替代品,我们推荐使用on()方法
    on() 方法在被选元素及子元素上添加一个或多个事件处理程序。
    使用 on() 方法添加的事件处理程序适用于当前及未来的元素
    如需添加只运行一次的事件然后移除,请使用 one()
    语法
    $(*selector*).on(*event,childSelector,data,function,map*)

  • bind() 方法为被选元素添加一个或多个事件处理程序,并规定事件发生时运行的函数
    $(selector).bind(event,data,function)

  • unbind() 方法移除被选元素的事件处理程序。
    该方法能够移除所有的或被选的事件处理程序,或者当事件发生时终止指定函数的运行。
    $(selector).unbind(event,function)

  • delegate
    delegate() 方法为指定的元素(属于被选元素的子元素)添加一个或多个事件处理程序,并规定当这些事件发生时运行的函数。
    使用 delegate() 方法的事件处理程序适用于当前或未来的元素(比如由脚本创建的新元素)。
    语法
    $(selector).delegate(childSelector,event,data,function)

  • live
    ive() 方法为被选元素附加一个或多个事件处理程序,并规定当这些事件发生时运行的函数。
    通过 live() 方法附加的事件处理程序适用于匹配选择器的当前及未来的元素(比如由脚本创建的新元素)。
    语法
    $(selector).live(event,data,function)

  • off
    off() 方法通常用于移除通过 on()方法添加的事件处理程序。
    自 jQuery 版本 1.7 起,off() 方法是 unbind()、die() 和 undelegate() 方法的新的替代品,推荐使用off()该方法

5:jquery 如何展示/隐藏元素?

  • 使用 hide() 和 show() 方法来隐藏和显示 HTML 元素:
//隐藏元素
$("#hide").click(function(){
  $("p").hide();
});

//显示元素
$("#show").click(function(){
  $("p").show();
});
  • 改变元素的css中的display属性
$(".box").css("display", "block");
    //设置元素的display为block
$(".box").css("display", "none");
    //设置元素的display为none
  • 用jQuery的fadeIn()、fadeOut()方法
$(".box").fadeIn();
   //fadeIn() 方法使用淡入效果来显示被选元素,假如该元素是隐藏的。
$(".box").fadeOut();
   //fadeOut() 方法使用淡出效果来隐藏被选元素,假如该元素是隐藏的。

6: jquery 动画如何使用?

  • animate() 方法执行 CSS 属性集的自定义动画。
    该方法通过CSS样式将元素从一个状态改变为另一个状态。CSS属性值是逐渐改变的,这样就可以创建动画效果。
    只有数字值可创建动画(比如 "margin:30px")。字符串值无法创建动画(比如 "background-color:red")。
    注释:使用 "+=" 或 "-=" 来创建相对动画(relative animations)。

语法1:
$(selector).animate(styles,speed,easing,callback)

styles: 必需,规定产生动画效果的 CSS 样式和值。
speed:可选,规定动画的速度
easing:可选,规定在不同的动画点中设置动画速度的 easing 函数
callback:可选,animate 函数执行完之后,要执行的函数。

语法 2
$(selector).animate(styles,options)

styles: 必需,规定产生动画效果的 CSS 样式和值。
options:可选,规定动画的额外选项

7:如何设置和获取元素内部 HTML 内容?如何设置和获取元素内部文本?

  • 获取元素内部 HTML 内容
    text() - 设置或返回所选元素的文本内容
    html() - 设置或返回所选元素的内容(包括 HTML 标记)
    val() - 设置或返回表单字段的值
$(".box").html() //获取元素内部的html内容,类似于innerHTML
$(".box").text() //获取元素内部的text文本,类似于innerText
  • 设置元素内部 HTML 内容和内部文本
$(".box").html("<p>设置了一个段落</p>")//设置了元素内部的html内容,标签生效
$(".box").text("设置了一个文本")//设置了元素内部的text文本,标签不生效

8:如何设置和获取表单用户输入或者选择的内容?如何设置和获取元素属性?

//获取
$(selectorOfFormData).val()//不加参数即可获取用户输入或选择的内容
//设置
$(selectorOfFormData).val(modifiedVal)//加参数即可设置用户输入或选择的内容
//获取
$(selector).attr(attributeName)//传入元素属性名即可获取元素的属性值

//设置单个属性
$(selector).attr(attributeName,attributeVal)//传入元素属性名和要设置的属性值即可完成对元素的属性设置

//用key:value的形式设置多个属性的值:
$( "img" ).attr({
  src: "/resources/hat.gif",
  title: "jQuery",
  alt: "jQuery Logo"
});

9: 使用 jquery实现如下效果

1.gif

预览代码

  • index() 方法返回指定元素相对于其他指定元素的 index 位置。

10: 使用 jquery 实现如下效果

预览代码

11: 实现如下效果

预览代码

AMD_CMD_RequireJS_模块化

1. 为什么要使用模块化?

1.解决命名冲突
2.解决文件依赖问题
3.模块的版本管理。通过别名等配置,配合构建工具,可以比较轻松地实现模块的版本管理。
4.提高可维护性。模块化可以让每个文件的职责单一,非常有利于代码的维护。
5.前端性能优化。通过异步加载模块,这对页面性能非常有益。
6.跨环境共享模块。CMD 模块定义规范与 Node.js 的模块规范非常相近。可以很方便实现模块的跨服务器和浏览器共享。
7.提高代码可读性,代码解耦,提高复用性
参考:玉伯也射雕github

2. CMD、AMD、CommonJS 规范分别指什么?有哪些应用

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

2.CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性,加载模块使用require 方法,该方法读取一个文件并执行,如果请求的模块不能返回,那么 require 必须抛出一个错误。

3.CommonJS模块的特点如下:
所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序

js标准教程

/*
模块定义model.js
*/ 

function printName(){
    console.log('a');
}
function sayHello(){
    console.log("hello" );
}
module.exports = {
    printName: printName,
    sayHello: sayHello
}
/*
加载模块
*/
var nameModule = require("./model.js")
nameModule.printName() //"a"
nameModule.sayHello() //"hello"

AMD
1.define和require这两个定义模块、调用模块的方法,合称为AMD模式。它的模块定义的方法非常清晰,不会污染全局环境,能够清楚地显示依赖关系,主要用于浏览器端。

2.AMD模式可以用于浏览器环境,并且允许非同步加载模块,也可以根据需要动态加载模块

3.requireJS主要解决两个问题:
a. 多个js文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器
b. js加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应时间越长

4.语法:
define(id?, dependencies?, factory);
require([dependencies], function(){});

//定义模块 myModule.js
define(['dependency'],function(){
    //定义方法,并return 出去
    var name = 'xx'
    function PrintName(){
        console.log(name);
    }
    return {
        PrintName:PrintName
    }
})
//加载模块
require(['mod'],function(my){
    my.PrintName();
})

/*
define(id?, dependencies?, factory);

A. id: 定义中模块的名字,可选;如果没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的名字。。
B. 依赖dependencies:是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。 定的参数个数调用工厂方法。
C. 工厂方法factory,模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值。

require([dependencies], function(){});

A. 第一个参数是一个数组,表示所依赖的模块
B. 第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块
require()函数在加载依赖的函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。
*/

CMD
CMD(Common Module Definition)是 SeaJS推广过程中产生的。在 CMD 规范中,一个模块就是一个文件。
define(id?, deps?, factory)
A. 一个文件一个模块,所以经常就用文件名作为模块id
B. CMD推崇依赖就近,所以一般不在define的参数中写依赖,在factory中写
factory有三个参数 function(require, exports, module)
require 是 factory 函数的第一个参数,require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口
Exports :exports
是一个对象,用来向外提供模块接口
Module:module
是一个对象,上面存储了与当前模块相关联的一些属性和方法

//定义模块 myModule.js
define(function(require, exports, module){
    var $ = require("jquery.js")
    $("div").addClass("active")
})

//加载模块
seajs.use(['myModule.js'], function(my){

})

##3. 使用 requirejs 完善入门任务15,包括如下功能:
1. 首屏大图为全屏轮播
2. 有回到顶部功能
3. 图片区使用瀑布流布局(图片高度不一),下部有加载更多按钮,点击加载更多会加载更多数据(数据在后端 mock)
4. 使用 r.js 打包应用

企业建站模块化_效果预览

gitHub_代码预览

demo.webpack.config.js

//官方最终版

const path = require('path');

module.exports = {
// click on the name of the option to get to the detailed documentation
// click on the items with arrows to show more examples / advanced options

entry: "./app/entry", // string | object | array
// Here the application starts executing
// and webpack starts bundling

output: {
// options related to how webpack emits results

path: path.resolve(__dirname, "dist"), // string
// the target directory for all output files
// must be an absolute path (use the Node.js path module)

filename: "bundle.js", // string
// the filename template for entry chunks

publicPath: "/assets/", // string
// the url to the output directory resolved relative to the HTML page

library: "MyLibrary", // string,
// the name of the exported library

libraryTarget: "umd", // universal module definition
// the type of the exported library

/* Advanced output configuration (click to show) */

},

module: {
// configuration regarding modules

rules: [
  // rules for modules (configure loaders, parser options, etc.)

  {
    test: /\.jsx?$/,
    include: [
      path.resolve(__dirname, "app")
    ],
    exclude: [
      path.resolve(__dirname, "app/demo-files")
    ],
    // these are matching conditions, each accepting a regular expression or string
    // test and include have the same behavior, both must be matched
    // exclude must not be matched (takes preferrence over test and include)
    // Best practices:
    // - Use RegExp only in test and for filename matching
    // - Use arrays of absolute paths in include and exclude
    // - Try to avoid exclude and prefer include

    issuer: { test, include, exclude },
    // conditions for the issuer (the origin of the import)

    enforce: "pre",
    enforce: "post",
    // flags to apply these rules, even if they are overridden (advanced option)

    loader: "babel-loader",
    // the loader which should be applied, it'll be resolved relative to the context
    // -loader suffix is no longer optional in webpack2 for clarity reasons
    // see webpack 1 upgrade guide

    options: {
      presets: ["es2015"]
    },
    // options for the loader
  },

  {
    test: "\.html$",

    use: [
      // apply multiple loaders and options
      "htmllint-loader",
      {
        loader: "html-loader",
        options: {
          /* ... */
        }
      }
    ]
  },

  { oneOf: [ /* rules */ ] },
  // only use one of these nested rules

  { rules: [ /* rules */ ] },
  // use all of these nested rules (combine with conditions to be useful)

  { resource: { and: [ /* conditions */ ] } },
  // matches only if all conditions are matched

  { resource: { or: [ /* conditions */ ] } },
  { resource: [ /* conditions */ ] },
  // matches if any condition is matched (default for arrays)

  { resource: { not: /* condition */ } }
  // matches if the condition is not matched
],

/* Advanced module configuration (click to show) */

},

resolve: {
// options for resolving module requests
// (does not apply to resolving to loaders)

modules: [
  "node_modules",
  path.resolve(__dirname, "app")
],
// directories where to look for modules

extensions: [".js", ".json", ".jsx", ".css"],
// extensions that are used

alias: {
  // a list of module name aliases

  "module": "new-module",
  // alias "module" -> "new-module" and "module/path/file" -> "new-module/path/file"

  "only-module$": "new-module",
  // alias "only-module" -> "new-module", but not "module/path/file" -> "new-module/path/file"

  "module": path.resolve(__dirname, "app/third/module.js"),
  // alias "module" -> "./app/third/module.js" and "module/file" results in error
  // modules aliases are imported relative to the current context
},
/* alternative alias syntax (click to show) */

/* Advanced resolve configuration (click to show) */

},

performance: {
hints: "warning", // enum
maxAssetSize: 200000, // int (in bytes),
maxEntrypointSize: 400000, // int (in bytes)
assetFilter: function(assetFilename) {
// Function predicate that provides asset filenames
return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
}
},

devtool: "source-map", // enum
// enhance debugging by adding meta info for the browser devtools
// source-map most detailed at the expense of build speed.

context: __dirname, // string (absolute path!)
// the home directory for webpack
// the entry and module.rules.loader option
// is resolved relative to this directory

target: "web", // enum
// the environment in which the bundle should run
// changes chunk loading behavior and available modules

externals: ["react", /^@angular//],
// Don't follow/bundle these modules, but request them at runtime from the environment

stats: "errors-only",
// lets you precisely control what bundle information gets displayed

devServer: {
proxy: { // proxy URLs to backend development server
'/api': 'http://localhost:3000'
},
contentBase: path.join(__dirname, 'public'), // boolean | string | array, static file location
compress: true, // enable gzip compression
historyApiFallback: true, // true for index.html upon 404, object for multiple paths
hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
https: false, // true for self-signed, object for cert authority
noInfo: true, // only errors & warns on hot reload
// ...
},

plugins: [
// ...
],
// list of additional plugins

/* Advanced configuration (click to show) */
}

js-函数与作用域

进阶3

1. 函数声明和函数表达式有什么区别?

  • 函数声明:定义一个具有指定参数的函数,函数声明最重要的特征就是函数声明提升,意思是在执行代码之前就会读取函数声明.

sayHi();
function sayHi(){
    alert("hi world");
}
//不会报错,因为函数声明在sayHi()在函数sayHi()之前已经读取

name :函数名
param:要传递给函数的参数的名称。不同引擎中的最大参数数量不同。一个函数最多有255个参数
statements:包含函数体的语句。
描述
一个被函数声明创建的函数是一个 Function 对象,具有 Function 对象的所有属性、方法和行为。
参考

  • 函数表达式
    用函数表达式定义的函数在使用之前必须先赋值
sayHi();
var sayHi = function() {
    alert("hi wrold");
}
//报错,函数sayHi()调用之前并未赋值
  • 区别
    1 函数表达式定义的函数在使用之前必须先赋值,而函数声明不必.
    2 函数表达式与函数声明的最主要区别是函数名称(function name),在函数表达式中可忽略它,从而创建匿名函数.

2. 什么是变量的声明前置?什么是函数的声明前置?

  • 变量声明前置就是在一个作用域块中,所有的变量都被放在块的开始出声明

1 var a = 1;
2 function main() {
3     console.log(a);//1
4 }
5 main();//输出1

1 var a = 1;
2 function main() {
3     console.log(a);
4     var a = 2;
5 }
6 main()//输出undefined

为什么输出undefined,因为脚本在执行的时候会自动将变量声明前置

参考

  • 函数的声明前置:在一个作用域下,同var 声明的变量一样,function 声明的函数也会前置。函数的声明前置优先级高于变量的声明前置。

var a = 3;
console.log(a); 
sayHello();
function sayHello(){
  console.log('hello');
}
执行时语句顺序如下:
var a
function sayHello(){}
console.log(a);//undefined
a=3
console.log(a); //3
sayHello();

3. arguments 是什么?

  • arguments对象是所有函数中可用的局部变量,你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数的条目,第一个条目的索引从0开始。例如,如果一个函数传递了三个参数,你可以参考它们如下:
arguments[0]
arguments[1]
arguments[2]

参数也可以被设置:
arguments[1] = 'new value';
arguments对象不是一个 Array它类似于数组

4. 函数的"重载"怎样实现?

  • Javascript中,先定义的函数,可以被后定义的函数覆盖。因此Javascript不支持函数的重载。比如下面的例子:
<script type="text/javascript">  
       function p(a, b, c) {  
           alert(a+b+c);  
       }  
 
       function p(a, b) {  
           alert(a+b);  
       }  
       p(1,2,3);//alert 6;  
   </script>  

虽然有函数p(a, b, c),但是由于语言的特性,该函数被后面的p(a,b)所覆盖.

  • 但是利用js的arguments,可以实现JavaScript的重载。
    function showMessage(){
         if(arguments.length==1){
             console.log(arguments[0]);
         }else if( arguments.length==2){
             console.log(arguments[0]+"说:"+arguments[1]);
         }else{
             return false;
         }
     }
    showMessage("Hi!");
    showMessage("张三","Hi 你好"); 

5. 立即执行函数表达式是什么?有什么作用

  • 我们需要在定义函数之后,立即调用该函数.这时不能在函数的定义之后加上圆括号,这会产生语法错误.
function(){ /* code */ }();
// SyntaxError: Unexpected token 

产生这个错误的原因是,function这个关键字即可以当作语句,也可以当作表达式。
// 语句
function f() {}
// 表达式
var f = function f() {}

为了避免解析上的歧义,JavaScript引擎规定,如果function关键字出现在行首,一律解释成语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。
解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。

  • 通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。
    它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
// 写法一
var tmp = newData;
processData(tmp);
storeData(tmp);

// 写法二
(function (){
  var tmp = newData;
  processData(tmp);
  storeData(tmp);
}());
上面代码中,写法二比写法一更好,因为完全避免了污染全局变量。

6. 求n!,用递归来实现


function jiechen(n) {
      if(n === 1){
      return 1;
    }
    return  n * jiechen(n-1);
}
 jiechen(4);//24

7. 以下代码输出什么?

	function getInfo(name, age, sex){
		console.log('name:',name);
		console.log('age:', age);
		console.log('sex:', sex);
		console.log(arguments);
		arguments[0] = 'valley';
		console.log('name', name);
	}

getInfo('饥人谷', 2, '男');
getInfo('小谷', 3);
getInfo('男');

输出:

name: 饥人谷
age: 2
sex: 男
["饥人谷", 2, "男"]
name valley

name: 小谷
age: 3
sex: undefined
["小谷", 3]
name valley

name: 男
age: undefined
sex: undefined
["男"]
name valley
undefined


8. 写一个函数,返回参数的平方和?

  function sumOfSquares() {
    var sum = 0;
    for (var i = 0; i < arguments.length; i++) {
        sum += (arguments[i]) * (arguments[i]);
    }

    return sum;
}

var result = sumOfSquares(2, 3, 4);
var result2 = sumOfSquares(1, 3);
console.log(result);  //29
console.log(result2); //10

9. 如下代码的输出?为什么

	console.log(a);
	var a = 1;
	console.log(b);

输出:
undefined //原因:变量声明前置var a; 
"error" //没有声明变量b


10. 如下代码的输出?为什么

	sayName('world');
	sayAge(10);
	function sayName(name){
		console.log('hello ', name);
	}
	var sayAge = function(age){
		console.log(age);
	};

输出:
"hello  world"

"error"  //报错原因:函数表达式定义的函数在使用之前必须先赋值.

11. 如下代码输出什么? 写出作用域链查找过程伪代码

:这里流程没用AO,Scope解释,因为不习惯.所以换成自己的语言.

var x = 10;
bar() ;
function foo() {
  console.log(x);
}
function bar(){
  var x = 30;
  foo();
}
输出:10
原因:
全局作用域: {
   可使用变量:   x : 10
   可使用的函数:  foo() , bar() 
}

foo()作用域: {
  可使用变量:   x : 10
}
bar()作用域: {
 可使用变量:   x : 30
 可使用的函数:  foo()
}
运行逻辑:
先申明全局变量 x = 10;
执行bar()函数;
申明一个仅在bar()作用域内访问的局部变量x=30;
执行foo()函数,执行console.log(x)方法;
但没在当前作用域下找到�x变量,但全局作用域的x=10可以使用,完成输出;


12. 如下代码输出什么? 写出作用域链查找过程伪代码

var x = 10;
bar();
function bar(){
  var x = 30;
  function foo(){
    console.log(x);
  }
  foo();
}	
输出:10 ,30
原因:
全局作用域: {
   可使用变量:   x : 10
   可使用的函数:   bar() 
}

foo()作用域: {
  可使用变量:   x : 10
}
bar()作用域: {
 可使用变量:   x : 30
 可使用的函数:  foo()
}
运行逻辑:
运行bar();
运行 foo(),从全局找到变量x,输出x=10;
运行第二个foo(),找到可使用的局部变量x=30,输出x=30;

13. 以下代码输出什么? 写出作用域链的查找过程伪代码

var x = 10;
bar(); 
function bar(){
  var x = 30;
  (function (){
    console.log(x);
  })();
}
输出:30
原因:
因为是立即调用函数所以上面代码等价于下面的:
var x = 10;
bar(); 
function bar(){
  var x = 30;
  function fn(){
    console.log(x);
  };
  fn();
}
全局作用域{
可用变量:x=10 ;
函数:bar()
}
bar()作用域{
可用变量 x=30
函数:fn()
}


14. 以下代码输出什么? 写出作用域链查找过程伪代码

var a = 1;

function fn(){
  console.log(a);//第一次输出:undefined:因为下面  var a = 5;所以提前申明了var a ;找不到a报错.
  var a = 5;
  console.log(a);//第二次输出5:因为在当前作用域下找到了a=5;
  a++;//a=6
  var a;
  fn3();
  fn2();
  console.log(a);//第五次输出:20,因为a的值已经在调用fn2()时被修改.

  function fn2(){
    console.log(a);//第四次输出:6,因为这作用域里没有var a 申明 ,去上一级即fn()里作用域找,此时a++后,a=6.所以输出6.
    a = 20;//  给fn()里的变量a赋值为20.并保存在内存里
  }
}

function fn3(){
  console.log(a);// 第三次输出:1,因为fn3()属于全局函数,这里没var a,就去全局找到var a =1
  a = 200; //找到了全局a ,在这一步给全局变量a 赋值为200.并保存在内存里.
}

fn();
console.log(a);//第六次输出:200,因为去全局找,而a的值已经在调用fn3()时被修改.

输出:
undefined
5
1
6
20
200

原因:
全局作用域{
  可用变量: a = 1
  可用函数: fn() ; fn3(); ;
}
fn()作用域内{
  可用变量: a =5; 
  可用函数:fn2()
}
fn2()作用域内{
  可用变量: a =20
}
fn3()作用域内{
  可用变量: a=200
}
运行逻辑:
代码//后内容:为解释

js-正则表达式

为什么要学习正则:正则表达式通常被用来检索、替换那些符合某个模式的文本. 1.凡是有需要用户填写表单的地方,都需要正则表达式做个前置验证 2.用户的输入永远是不安全的 3.前端的过滤只能阻挡90%「粗心」的用户,而剩下10%的「恶意」用户还需要后端再次做一次验证来阻挡

题目1: \d,\w,\s,[a-zA-Z0-9],\b,.,*,+,?,x{3},^,$分别是什么?

\d: 匹配数字0-9

\w: 匹配字母,数字,下划线,等价于于[a-zA-Z_0-9]

\s:空白符

[a-zA-Z0-9]: a到z,A到Z,0到9三者的集合.等价于\w

\b:单词边界

. :除了回车符和换行符之外的所有字符

*:量词,现零次或多次(任意次)

** + ** :量词,出现一次或多次(至少出现一次)

? : 量词,出现零次或一次(最多出现一次)

x{3} :出现3次 'x'字符

^ :例如^xx是匹配以xx开头,但在[^]里代表取反.

$ :例如xx$是匹配以xx结尾

元字符

  • 正则表达式让人望而却步以一个重要原因就是转义字符太多了,组合非常多,但是正则表达式的元字符(在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符)并不多

( [ { \ ^ $ | ) ? * + .

Paste_Image.png

Paste_Image.png

参考


题目2: 写一个函数trim(str),去除字符串两边的空白字符

str1 = '  asd  ';
function fn(str){
  var reg = /^\s+|\s+$/g;//这里不能是字符串形式
  return str.replace(reg,'');
}
console.log(fn(str1));//'asd'

题目3: 写一个函数isEmail(str),判断用户输入的是不是邮箱

function isEmail(str){
  var reg = /^[a-zA-Z0-9]+@[a-zA-Z0-9]+(\.com)$/;//(\.com).是元字符,必须加/转为普通字符
   return reg.test(str);
}
console.log (isEmail('[email protected]'));//ture

题目4: 写一个函数isPhoneNum(str),判断用户输入的是不是手机号

function isPhoneNum(str){
  var reg = /^(\+86)?1[34578]\d{9}$/;
  return reg.test(str);
}

console.log(isPhoneNum('+8613844446666'));
```

## 题目5: 写一个函数isValidUsername(str),判断用户输入的是不是合法的用户名(长度6-20个字符,只能包括字母、数字、下划线)

```

function isValidUsername(str){
  var reg = /^\w{6,20}$/;
  return reg.test(str);
}

console.log(isValidUsername('as123_s'));
```



## 题目6: 写一个函数isValidPassword(str), 判断用户输入的是不是合法密码(长度6-20个字符,只包括大写字母、小写字母、数字、下划线,且至少至少包括两种)

```
function isValidPassword(str){
  var reg = /\W/;
  
  if(str.length<6 || str.length>20 || reg.test(str)){
    return false;   
  }
  var n = 0;
  if(/[a-z]/.test(str)) n++;
  if(/[A-Z]/.test(str)) n++;
  if(/[0-9]/.test(str)) n++;
  if(/_/.test(str)) n++;
  if(n >=2){ 
    return true;
  }else{
    return false;
  }

}

console.log(isValidPassword('asd_as'));

```


## 题目7: 写一个正则表达式,得到如下字符串里所有的颜色

```
var re = /#[0-9a-zA-Z]{6}/g;
var subj = "color: #121212,background-color: #AA00ef,width: 12px; bad-colors: f#fddee";

console.log( subj.match(re) );  // ['#121212', '#AA00ef']
```


## 题目8: 下面代码输出什么? 为什么? 
```
var str = 'hello  "hunger" , hello "world"';
var pat =  /".*"/g;
str.match(pat);//打印: ""hunger" , hello "world"",因为*是出现任意次,会开启贪婪模式匹配尽可能多,于是一直到结尾.
```

- 改写代码,让其输出:[""hunger"", ""world""].

```
var str = 'hello  "hunger" , hello "world"';
var pat =  /".*?"/g;//加?非贪婪模式.
str.match(pat);//""hunger"", ""world""
```

---
[参考](http://book.jirengu.com/fe/%E5%89%8D%E7%AB%AF%E5%9F%BA%E7%A1%80/Javascript/%E6%AD%A3%E5%88%99%E7%9B%B8%E5%85%B3%E6%96%B9%E6%B3%95.html)

媒体查询_flex布局_栅格系统

1.flex

  • 一个容器设置了display:flex;属性就定义了一个flex容器,它的直接子元素会接受这个flex环境
    .container { display: flex; }

  • 设为 Flex 布局以后,子元素的float、clear和vertical-align属性将失效

  • flex兼容性如下图:
    缺点:支持该属性的浏览器版本并不多.
    image.png

flex参考

2.媒体查询

常用的媒体类型有:

  1. all(所有),适用于所有设备。
  2. handheld(手持),用于手持设备。
  3. print(印刷),用于分页材料以及打印预览模式下在屏幕上的文档视图。
  4. projection(投影),用于投影演示文稿,例如投影仪。
  5. screen(屏幕) ,主要用于计算机屏幕。
    在使用的时候可以在样式表直接书写 @media指令+空格+媒体类型(多个逗号隔开)

有以下常用的media feature

width:浏览器宽度

height:浏览器高度

device-width:设备屏幕分辨率的宽度值

device-height:设备屏幕分辨率的高度值

orientation:浏览器窗口的方向纵向还是横向,当窗口的高度值大于等于宽度时该特性值为portrait,否则为landscape

如何引入media:有两种常用的引入方式

  • link方法引入
<link rel="stylesheet" type="text/css" href="styleB.css"  media="screen and (min-width: 600px) and (max-width: 800px)">
@media screen and (min-width: 600px) and (max-width: 800px){
    选择器{
        属性:属性值;
    }
}
CSS2:
@media print {
   body { font-size: 10pt }
 }
 @media screen {
   body { font-size: 13px }parsing-errors
 }
 @media screen, print {
   body { line-height: 1.2 }
 }

CSS3:
@media screen and (max-width: 990px){
    .container{
        background: orange;
    }
}

3.用媒体查询实现如下要求

1. 在页面宽度> 1200px 时页面背景为红色 
2. 在页面1200px>=宽度> 900px  时页面背景为绿色
3. 在页面900px>=宽度> 600px  时页面背景为黄色
4. 宽度<=600px 背景为灰色

代码预览

4.实现一个简单的栅格系统

代码预览

HTTP详解

1.OSI 七层模型指什么

概念:开放式系统互联通信参考模型,简称为OSI模型,一个试图使各种计算机在世界范围内互连为网络的标准框架.

OSI 七层模型

2.HTTP 的工作原理是什么?

HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。下图表明了这种请求/响应模型。
 
HTTP工作原理
 
以下是HTTP请求/响应的步骤:
(1)客户端连接到Web服务器
一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。例如,http://www.oakcms.cn。
(2)发送HTTP请求
通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。
(3)服务器接受请求并返回HTTP响应
Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
(4)释放连接TCP连接
Web服务器主动关闭TCP套接字,释放TCP连接;客户端被动关闭TCP套接字,释放TCP连接。
(5)客户端浏览器解析HTML内容
客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

3.URI 的格式是什么?常见的协议有哪些

格式:
如用客户端浏览器请求这个页面:http://localhost.com:8080/index.htm
     从中分解出协议名、主机名、端口、对象路径等部分,对于我们的这个地址,解析得到的结果如下:     协议名:http     主机名:localhost.com     端口:8080     对象路径:/index.htm

常见的协议:
HTTP、HTTPS、FTP

4.HTTP 协议有几种和服务器交互的方法

交互的方法:
1.GET:请求服务器发送某个资源。
2.POST:向服务器发送数据,通常用于支持HTML的表单input等,表单中的数据会被发送到服务器。
3.HEAD:HEAD方法与GET类似,但是在服务器的响应中没有资源的内容,只有资源的一些基本信息,主要用于查看资源大小、类型,是否存在,查看资源是否被修改。
4.PUT:跟GET从服务器获取资源相反、它是用于给服务器写入一个资源。
5.TRACE: 用于查看发送到服务器的请求是否被服务器接收或者在发送过程中被修改。
6.DELETE:请求服务器删除对应URL。
7.OPTIONS:请求查阅服务器性能或查阅资源相关的选项需求

5.http和 https是什么?有什么区别?

https, 全称Hyper Text Transfer Protocol Secure,相比http,多了一个secure,保护的意思,https和http都属于application layer,基于TCP(以及UDP)协议,但是又完全不一样。
总体来说,https和http类似,但是比http安全那https具体保护了啥?保护了你从连接到这个网站开始,
到你关闭这个页面为止,你和这个网站之间收发的所有信息,
不会发生你输入www.google.com,实际上跑到了另一个网站去了。
https防止DNS攻击

HTTP的缺点:
通信使用明文(不加密), 内容可能会被窃听
不验证通信方的身份, 因此有可能遭遇伪装
无法证明报文的完整性, 所有有可能已遭篡改

HTTP+加密+认证+完整性保护 = HTTPS

原理:
HTTPS并非是应用层的一种新协议. 只是HTTP通信接口部分用SLL(Secure Socket Layer)和TLS (Transport Layer Security) 协议替代而已.

通常, HTTP直接和TCP通信, 当使用SSL时, 演变成了先和SSL通信, 再由SSL和TCP通信了, 简而言之, 所谓HTTPS, 其实就是身披SSL协议的这层外壳的HTTP.

在采用SSL后, HTTP就拥有了HTTPS的加密, 证书和完整性的保护这些功能.

SSL是独立于HTTP的协议, 所有不光是HTTP协议, 其他运行在应用层的SMTP(邮件协议)和Telnet等协议均可配合SSL协议使用. 可以说SSL是当今世界上应用最广泛的网络安全技术.
SSL是如何加密的

SSL采用一种叫做公开密钥加密(Public-key cryptography)的加密方式.

近代的加密方法中, 加密算法是公开的, 而秘钥是保密的, 通过这种方式得以保持加密方法的安全性.

既然HTTPS那么的可靠安全, 那为何不所有的Web网站不一直使用HTTPS?
其中的一个原因是, 因为与纯文本通信相比, 加密通信会消耗更多的CPU资源以及内存资源, 如果每次通信都加密, 会消耗相当多的资源, 平摊到一台计算机上时, 能够处理的请求数量必定会随之减少.
因此, 如果是非敏感信息则使用HTTP通信, 只有在包括个人信息等敏感数据时, 才利用HTTPS加密通信, 以节省资源. 除此之外, 想要节约购买证书的开销也原因之一.

HTTPS和HTTP的区别:
https协议需要到ca申请证书,一般免费证书很少,需要交费。
http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的。
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议 要比http协议安全。

6.状态码含义

200:OK客户端请求成功。
301:重定向,URI发生了改变浏览器自动访问新的URL。
304:跟上次访问的缓存相同,直接使用上次缓存。
403:禁止访问权限不够。
404:服务器找不到请求的资源。
500:服务器执行请求发生错误
503:服务器超出载荷或者停机维护

参考

7.报文有哪几部分组成?

 一个HTTP报文由3部分组成,分别是:  
(1)、描述性的起始行(start line)  
(2)、包含属性的首部(header)  
(3)、包含数据的主体(body)  示例:
HTTP/1.0 200 OK //起始行Content-type:text/plain //首部Content-length:19 //首部 Hi I'm a message! 主体

8.请求头的格式和作用是什么?

请求报文与响应报文的格式请求报文的格式:

响应报文的格式:

9.首部的格式和作用是什么?给个范例截图说明

服务器返回给客户端的文本信息

11.简述浏览器缓存是如何控制的

初次访问

再次访问

参考

12. 下图各个参数是什么?

HTML5_CSS3知识

为什么设计HTML5规范? 1.HTML5的设计目的是为了在移动设备上支持多媒体 2.

1. HTML5是什么?有哪些新特性?有哪些新增标签?如何让低版本的 IE 支持 HTML5新标签?CSS3新特性有哪些?

  • HTML5是超文本标记语言的第五次重大修改,2014年10月29日标准规范制定完成.
    新特性:
  • 语义:能够让你更恰当地描述你的内容是什么.
  • 连通性:能够让你和服务器之间通过创新的新技术方法进行通信。
  • 离线 & 存储:能够让网页在客户端本地存储数据以及更高效地离线运行。
  • 多媒体:使 video 和 audio 成为了在所有 Web 中的一等公民。
  • 2D/3D 绘图 & 效果:提供了一个更加分化范围的呈现选择。
  • 性能 & 集成:提供了非常显著的性能优化和更有效的计算机硬件使用。
  • 设备访问 Device Access:能够处理各种输入和输出设备。
  • 样式设计: 让作者们来创作更加复杂的主题.
  • Html5取代Flash在移动设备的地位
  • websocket:是一种协议,可以让我们建立客户端到服务器端的全双工通信,这就意味着服务器端可以主动推送数据到客户端

新增标签
canvas 标签定义图形,比如图表和其他图像。该标签基于 JavaScript 的绘图 API
audio 定义音频内容
video 定义视频(video 或者 movie)
output 定义不同类型的输出,比如脚本的输出
article 定义页面正文内容
aside 定义页面内容之外的内容
footer 定义 section 或 document 的页脚
header 定义了文档的头部区域
nav 导航
progress 定义任何类型的任务的进度
section 定义文档中的节(section、区段)

如何让低版本的 IE 支持 HTML5新标签?

  • 针对于IE6-9,Safari 4.x和FF 3.x的HTML5元素的基本样式.
    使用html5shiv
//安装:bower install html5shiv --save-dev
<! - [if lt IE 9]> 
	<script src =“bower_components / html5shiv / dist / html5shiv.js”> </ script> 
<![endif] - >

html5/css3参考

2.input 有哪些新增类型?

button 定义可点击的按钮(通常与 JavaScript 一起使用来启动脚本)。
checkbox 定义复选框。
email 定义用于 e-mail 地址的字段。
file 定义文件选择字段和 "浏览..." 按钮,供文件上传。
hidden 定义隐藏输入字段。
image 定义图像作为提交按钮。
number 定义用于输入数字的字段。
password 定义密码字段(字段中的字符会被遮蔽)。
radio 定义单选按钮。
range 定义用于精确值不重要的输入数字的控件(比如 slider 控件)。
reset 定义重置按钮(重置所有的表单值为默认值)。
search 定义用于输入搜索字符串的文本字段。
submit 定义提交按钮。
tel 定义用于输入电话号码的字段。
text 默认。定义一个单行的文本字段(默认宽度为 20 个字符)。
time 定义用于输入时间的控件(不带时区)。
url 定义用于输入 URL 的字段。

//search
<input type="number" name="quantity" min="1" max="5">
//search
<input type="search" name="googlesearch">
//E-mail
<input type="email" name="usremail">

参考

3.浏览器本地存储中 cookie 和 localStorage和sessionStorage 有什么区别?localStorage 如何存储删除数据?

Cookie

  • Cookie 是小甜饼的意思。顾名思义,cookie 确实非常小,它的大小限制为4KB左右,是网景公司的前雇员 Lou Montulli 在1993年3月的发明。它的主要用途有保存登录信息,比如你登录某个网站市场可以看到“记住密码”,这通常就是通过在 Cookie 中存入一段辨别用户身份的数据来实现的。

localStorage

  • localStorage 是 HTML5 标准中新加入的技术,它并不是什么划时代的新东西。早在 IE 6 时代,就有一个叫 userData 的东西用于本地存储,而当时考虑到浏览器兼容性,更通用的方案是使用 Flash。而如今,localStorage 被大多数浏览器所支持,如果你的网站需要支持 IE6+,那以 userData 作为你的 polyfill 的方案是种不错的选择。

**sessionStorage 与 localStorage **的接口类似,但保存数据的生命周期与 localStorage 不同。做过后端开发的同学应该知道 Session 这个词的意思,直译过来是“会话”。而 sessionStorage 是一个前端的概念,它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但当页面关闭后,sessionStorage 中的数据就会被清空。

三者的异同
image.png

localStorage和sessionStorage都具有相同的操作方法

  • 存储key: setItem("key","value").接收2个参数
sessionStorage.setItem("key", "value");     
localStorage.setItem("name", "tom");

  • 获取指定key本地存储的值: getItem(key)
var value1 = sessionStorage.getItem("key");     
var site = localStorage.getItem("name");//'tom'
  • removeItem删除key: 删除指定key本地存储的值
sessionStorage.removeItem("key"); 
localStorage.removeItem("site");
  • clear清除所有的key/value
sessionStorage.clear();    
localStorage.clear();

4.写出如下 CSS3效果的简单事例

  1. 圆角, 圆形
  2. div 阴影
  3. 2D 转换:放大、缩小、偏移、旋转
  4. 3D 转换:移动、旋转
  5. 背景色渐变
  6. 过渡效果
  7. loading圆环动画
    --->代码预览

5.实现全屏图加过渡色的效果

代码预览

6. loading 动画效果demo

 效果 
代码

npm_npmscript前端工作流_gulp_webpack

1.npm install _本地安装与全局安装的区别?

npm的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有-g而已,
比如:
npm install grunt # 本地安装
npm install -g grunt-cli # 全局安装

这两种安装方式有什么区别呢?从npm官方文档的说明来看,主要区别在于(后面通过具体的例子来说明):
本地安装

  1. 将安装包放在 ./node_modules 下(运行npm时所在的目录)
  2. 可以通过 require() 来引入本地安装的包

全局安装

  1. 将安装包放在 /usr/local 下
  2. 可以直接在命令行里使用

2.npm是什么?

NPM(node package manager),通常称为node包管理器。顾名思义,它的主要功能就是管理node包,包括:安装、卸载、更新、查看、搜索、发布等。

npm的背后,是基于couchdb的一个数据库,详细记录了每个包的信息,包括作者、版本、依赖、授权信息等。它的一个很重要的作用就是:将开发者从繁琐的包管理工作(版本、依赖等)中解放出来,更加专注于功能的开发。

3.如何全局安装一个 node 应用?

npm install -g <module-name>

4.package.json 有什么作用?

每个项目的根目录下面,一般都有一个 package.json 文件,定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。npm install 命令根据这个配置文件,自动下载所需的模块,也就是配置项目所需的运行和开发环境。

{
  "name": "test",  //名称
  "version": "0.0.1",  //版本
  "description": "This is my first node.js program.",  //描述
  "main": "index.js",  //入口
  "keywords": [  //关键字
                       "node.js",
                       "javascript"
  ],
  "scripts": {  //执行命令行
	  "start": "node index.js"
  },
  "author": "Mike",  //作者
  "license":"MIT",  //认证
  "dependencies": {  //生产环境依赖
	              "express": "latest"
  },
  "devDependencies": {  //开发环境依赖
		   "bower": "~1.2.8",
		   "grunt": "~0.4.1"
  }
}

5.npm install --save app 与 npm install --save-dev app有什么区别?

--save 将产品运行时(或生产环境)需要的依赖模块添加到 package.json 的 dependencies 中,
在发布后还需要继续使用,否则就运行不了。
--save-dev 将产品的开发环境需要的依赖模块添加到 package.json 的 devDependencies 中,
只在开发时才用到,发布后用不到它。

6.node_modules的查找路径是怎样的?

从当前文件目录开始查找node_modules目录;然后依次进入父目录,查找父目录下的node_modules目录;依次迭代,直到根目录下的node_modules目录。比如某个模块的绝对路径是/home/user/foo.js,在该模块中使用require('bar')方式加载模块时,node将在下面的位置进行搜索:

/node_modules/bar
/home/node_modules/bar
/home/user/node_modules/bar

7.npm3与 npm2相比有什么改进?yarn和 npm 相比有什么优势?

参考

  • 针对 npm2 的问题,npm3 加了点算法,npm install 时会按照 package.json 里依赖的顺序依次解析,遇到新的包就把它放在第一级目录,后面如果遇到一级目录已经存在的包,会先判断版本,如果版本一样则忽略,否则会按照 npm2 的方式依次挂在依赖包目录下

8.npm script是什么?如何使用?

package.json 文件有一个 scripts 字段,可以用于指定脚本命令,供 npm 直接调用。npm 内置了两个简写的命令:npm test 和 npm start,其它命令要写成 npm run xxx 形式。

9.webpack是什么?和其他同类型工具比有什么优势?

webpack是一款模块加载器兼打包工具,它能把各种资源JS/CSS/图片等都作为模块来使用和处理。优势如下:

webpack 是以 commonJS 的形式来书写脚本,但对 AMD/CMD 的支持也很全面,方便旧项目进行代码迁移。
webpack可以将代码拆分成多个区块,每个区块包含一个或多个模块,它们可以按需异步加载,极大地减少了页面初次加载时间。
webpack 本身只能处理原生的 JS 模块,但是 loader 转换器可以将各种类型的资源转换成 JS 模块。这样,任何资源都可以成为 webpack 可以处理的模块。
webpack 有一个智能解析器,几乎可以处理任何第三方库,无论它们的模块形式是 CommonJS、 AMD 还是普通的 JS 文件。
webpack 还有一个功能丰富的插件系统。大多数内容功能都是基于这个插件系统运行的,还可以开发和使用开源的 webpack 插件,来满足各式各样的需求。
webpack使用异步 I/O 和多级缓存提高运行效率,使得它能够快速增量编译。

** requirejs和webpack区别**
requirejs 只支持 AMD
webpack 支持 AMD 和commonJS 和 ES modules

10.Grunt是什么,gulp是什么?

  • 在Javascript的开发过程中,经常会遇到一些重复性的任务,比如合并文件、压缩代码、检查语法错误、将Sass代码转成CSS代码等等。通常,我们需要使用不同的工具,来完成不同的任务,既重复劳动又非常耗时。Grunt就是为了解决这个问题而发明的工具,可以帮助我们自动管理和运行各种任务。
    简单说,Grunt是一个自动任务运行器,会按照预先设定的顺序自动运行一系列的任务。这可以简化工作流程,减轻重复性工作带来的负担。

  • Gulp与Grunt一样,也是一个自动任务运行器。它充分借鉴了Unix操作系统的管道(pipe)**,很多人认为,在操作上,它要比Grunt简单.

11.使用 gulp 实现图片压缩、CSS 压缩合并、JS 压缩合并?

//安装插件
npm install gulp-imagemin --save-dev //图片压缩
npm install gulp-cssnano --save-dev //css压缩
npm install uglify --save-dev //js压缩
npm install gulp-jshint --save-dev //js规范检查
npm install gulp-concat --save-dev //文件合并
npm install gulp-rename --save-dev //重命名

//gulpfile.js
//引入插件
var gulp = require('gulp'),
    cssnano = require('gulp-cssnano'),
    concat = require('gulp-concat'),
    jshint = require('gulp-jshint'),
    uglify = require('gulp-uglify'),
    imagemin = require('gulp-imagemin'),
    rename = require('gulp-rename'),
 
  //css合并压缩
  gulp.task('build:css', function() {
      gulp.src('./src/css/*.css')
        .pipe(concat('merge.css'))
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(cssnano())
        .pipe(gulp.dest('dist/css/'));
  })

  //js合并压缩
   gulp.task('build:js', function() {
      gulp.src('src/js/*.js')
        .pipe(jshint())
        .pipe(jshint.reporter('default'))
        .pipe(concat('merge.js'))
        .pipe(rename({
          suffix: '.min'
        }))
        .pipe(uglify())
        .pipe(gulp.dest('dist/js/'));
  })

  //图片压缩
  gulp.task('build:image', function() {
      gulp.src('src/imgs/*')
        .pipe(imagemin())
        .pipe(gulp.dest('dist/imgs/'));
  })

gulp.task('build', ['build:css', 'build:js', 'build:image']);

//命令行
gulp build

12.使用 webpack 替换企业建站中模块化使用的 requrieJS.

效果预览
代码

更多webpack个人工作笔记参考

jQuery动画__ jQuery ajax

1. jQuery 中, $(document).ready()是什么意思?

  • 当DOM准备就绪时,指定一个函数来执行。
  • 与JavaScript提供了window.onload事件的区别
    window.onload是当页面呈现时用来执行这个事件,直到所有的东西,如图像已被完全接收前,此事件不会被触发
    $(document).ready()只要DOM结构已完全加载时,脚本就可以运行。传递处理函数给.ready()方法,能保证DOM准备好后就执行这个函数

2.$node.html()和$node.text()的区别?

  • $node.html()
    获取集合中第一个匹配元素的HTML内容
    在一个 HTML 文档中, 我们可以使用 .html() 方法来获取任意一个元素的内容。 如果选择器匹配多个元素,那么只有第一个匹配元素的 HTML 内容会被获取
    用法:
    .html()
    .html( htmlString )
    .html( function(index, oldhtml) )

  • $node.text()
    得到匹配元素集合中每个元素的文本内容结合,包括他们的后代

  • 和 .html() 方法不同, .text() 在XML 和 HTML 文档中都能使用。.text() 方法返回一个字符串,包含所有匹配元素的合并文本

3.$.extend 的作用和用法? 

  • 描述: 将两个或更多对象的内容合并到第一个对象。

  • jQuery.extend( [deep ], target, object1 [, objectN ] )

deep
类型: [Boolean]
如果是true,合并成为递归(又叫做深拷贝)。

target
类型: [Object]
对象扩展。这将接收新的属性。

object1
类型: [Object]
一个对象,它包含额外的属性合并到第一个参数.

objectN
类型: [Object]
包含额外的属性合并到第一个参数

当我们提供两个或多个对象给$.extend(),对象的所有属性都添加到目标对象(target参数)。

如果只有一个参数提供给$.extend(),这意味着目标参数被省略。在这种情况下,jQuery对象本身被默认为目标对象。这样,我们可以在jQuery的命名空间下添加新的功能。这对于插件开发者希望向 jQuery 中添加新函数时是很有用的。

请记住,目标对象(第一个参数)将被修改,并且将通过$.extend()返回。然而,如果我们想保留原对象,我们可以通过传递一个空对象作为目标对象

var object = $.extend({}, object1, object2);```
`在默认情况下,通过$.extend()合并操作不是递归的;`如果第一个对象的属性本身是一个对象或数组,那么它将完全用第二个对象相同的key重写一个属性。这些值不会被合并。`如果将 true 作为该函数的第一个参数,那么会在对象上进行递归的合并。`
`警告:不支持第一个参数传递 false `


## 4.jQuery 的链式调用是什么?
- jQuery高效的原因之一就是其链式调用。
`链式调用原理:`就是通过对象上每个方法最后返回本对象__this。
可以调用对此对象的其他jQuery方法,实现连续调用多个方法

- `链式调用的好处`
节省代码量,代码看起来更清晰优雅;
可以提高代码的效率。
因为Javascript是无阻塞语言,通过事件来驱动,异步来完成一些本需要阻塞进程的操作。异步编程,编写代码时也是分离的,这就使代码可读性变差。
而链式操作,代码流程清晰,改善了异步体验。

例如:
$("#Test").addClass('style').find("div").eq(0).fadeOut(200);



## 5.jQuery 中 data 函数的作用?
- **描述: **存储任意数据到指定的元素,返回设置的值。
- jQuery.data() 方法允许我们在DOM元素上附加任意类型的数据,避免了循环引用的内存泄漏风险。如果 DOM 元素是通过 jQuery 方法删除的或者当用户离开页面时,jQuery 同时也会移除添加在上面的数据。我们可以在一个元素上设置不同的值,并获取这些值:

jQuery.data(document.body, 'foo', 52);
jQuery.data(document.body, 'bar', 'test');


## 6.写出以下功能对应的 jQuery 方法:

给元素 $node 添加 class active:
$node.addClass('active')
给元素 $noed 删除 class active:
$node.removeClass('actice')


展示元素$node:
$node.show()
隐藏元素$node:
$node.hide()

- 获取元素$node 的 属性: id、src、title, 修改以上属性

jQuery.attr():获取匹配的元素集合中的第一个元素的属性的值 或 设置每一个匹配元素的一个或多个属性。

$node.attr('id')//获取
$node.attr('id','value');//修改并赋值为value
$node.attr('src');//获取
$node.attr('src','value');//修改
$node.attr('title');//获取
$node.attr('title','value');//修改


- 给$node 添加自定义属性data-src
`$node.data('src','value')`

- 在$ct 内部最开头添加元素$node
`$ct.prepend('$node')`
- .prepend()
方法将指定元素插入到匹配元素里面作为它的第一个子元素
- .prepend()和[.prependTo()]实现同样的功能,主要的不同是语法,插入的内容和目标的位置不同。 

- 在$ct 内部最末尾添加元素$node
'$ct.append('$node')'
- 如果要作为最后一个子元素插入用[.append()]

- 删除$node
`$node.remove()`

- 把$ct里内容清空
`$ct.empty()`

- 在$ct 里设置 html <div class="btn"></div>
'$ct.html(' html <div class="btn"></div>')'

- 获取、设置$node 的宽度、高度(分别不包括内边距、包括内边距、包括边框、包括外边距)

$node.width();//仅包括内容
$node.height();//仅包括内容
$node.width(10px);//仅包括内容设置为10px

$node.innerWidth();//包括内容和内边距宽度
$node.innerHeight();//包括内容和内边距高度
$node.innerWidth(10px);//设置

$node.outerWidth();//包括内容,内边距,边框宽度
$node.outerHeight();//包括内容,内边距,边框高度
$node.outerWidth(10px);//设置

$node.outerHeight(true);//包括内容,内边距,边框,外边距高度
$node.outerWidth(true);//包括内容,内边距,边框,外边距宽度


- 获取窗口滚动条垂直滚动距离
`$(window).scrollTop()`

- 获取$node 到根节点水平、垂直偏移距离
`$node.offset().left;//水平距离`
`$node.offset().top;//垂直距离`
offset(): 在匹配的元素集合中,获取的第一个元素的当前坐标,坐标相对于文档。

- 修改$node 的样式,字体颜色设置红色,字体大小设置14px

- ```
$node.css({
'color':'red',
 'font-size': '14px'
})
  • 遍历节点,把每个节点里面的文本内容重复一遍
$.each(function(){
  console.log($(this).text())
})
  • 从$ct 里查找 class 为 .item
    的子元素
    $ct.find('.item')

  • 获取$ct 里面的所有孩子
    $ct.children()

  • 对于$node,向上找到 class 为'.ct'的父亲,在从该父亲找到'.panel'的孩子
    $node.parents('.ct').children('.panel')

  • 获取选择元素的数量
    $node.length

  • 获取当前元素在兄弟中的排行
    $node.index()

7. 应用

用jQuery实现以下操作当点击$btn 时,让 $btn 的背景色变为红色再变为蓝色 当窗口滚动时,获取垂直滚动距离 当鼠标放置到$div 上,把$div 背景色改为红色,移出鼠标背景色变为白色 当鼠标激活 input 输入框时让输入框边框变为蓝色,当输入框内容改变时把输入框里的文字小写变为大写,当输入框失去焦点时去掉边框蓝色,控制台展示输入框里的文字 当选择 select 后,获取用户选择的内容

预览
代码

8.用 jQuery ajax 实现如下效果。当点击加载更多会加载数据展示到页面.

效果预览:
加载更多

html
router.js

9.实现一个天气预报页面,前端展示自由发挥,数据接口: http://api.jirengu.com/weather.php

天气api

this_原型链_继承

this

问题1: apply、call 、bind有什么作用,什么区别

  • call 和 apply 都是为了改变某个函数运行时的上下(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向
    第一个参数都是希望设置的this对象,不同之处在于call方法接收参数列表,而apply接收参数数组
    func.call(this, arg1, arg2); func.apply(this, [arg1, arg2])

-** bind**
bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数

var foo = {
    bar : 1,
    eventBind: function(){
        var _this = this;
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(_this.bar);     //1
        });
    }
}

区别是:当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。

总结一下:
apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后续参数传参;
bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
参考

问题2: 以下代码输出什么?

var john = { 
  firstName: "John" 
}
function func() { 
  alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi() //输出:John:hi!

问题3: 下面代码输出什么,为什么

func() 
function func() { 
  alert(this)//输出 [object window],因为在函数被直接调用时this绑定到全局对象,在浏览器中,window 就是该全局对象
}

问题4:下面代码输出什么

document.addEventListener('click', function(e){
    console.log(this);//document
    setTimeout(function(){
        console.log(this);//window:setTimeout里的this指向window
    }, 200);
}, false);

问题5:下面代码输出什么,为什么

var john = { 
  firstName: "John" 
}

function func() { 
  alert( this.firstName )
}
func.call(john)//John因为func.call(john)的this就是第一个参数john

问题6: 以下代码有什么问题,如何修改

var module= {
  bind: function(){
    $btn.on('click', function(){
      console.log(this) //this指$btn
      this.showMsg();
    })
  },
   
  showMsg: function(){
    console.log('hunger');
  }
}
问题
//在事件处理程序中this代表事件源DOM对象,所以该处的this指向$btn
//this.showMsg() 同理这里的this指向$btn,所以这里会出错,因为$btn中没有showMsg()方法应该为如下
修改后:
var module= {
  bind: function(){
    var that = this;//引用this
    $btn.on('click', function(){
      console.log(that) 
      that.showMsg();//this为module
    })
  },
  showMsg: function(){
    console.log('hunger');
  }
}

原型链相关问题

问题7:有如下代码,解释Person、 prototype、proto、p、constructor之间的关联。

function Person(name){
    this.name = name;
}
Person.prototype.sayName = function(){
    console.log('My name is :' + this.name);
}
var p = new Person("若愚")
p.sayName();

通过函数定义类Person,类自动获得属性prototype,p是Person类的实例,有一个内部属性_proto_,
他指向类的prototype,p._proto_.constructor指向构造函数Person。

问题8: 上例中,1.对象 p可以这样调用p.toString(),toString是哪里来的? 2.画出原型图? 3.解释什么是原型链

image.png

image.png

3.原型链

首先来说说prototype属性:不像每个对象都有__proto__属性来标识自己所继承的原型,只有函数才有prototype属性。
JS不像其它面向对象的语言,它没有类(class,ES6引进了这个关键字)的概念。JS通过函数来模拟类。
当你创建函数时,JS会为这个函数自动添加prototype属性,值是空对象。而一旦你把这个函数当作构造函数(constructor)调用(即通过new关键字调用),那么JS就会帮你创建该构造函数的实例,实例继承构造函数prototype的所有属性和方法(实例通过设置自己的__proto__指向承构造函数的prototype来实现这种继承)。

JS正是通过__proto__和prototype的合作实现了原型链,以及对象的继承

构造函数,通过prototype来存储要共享的属性和方法,也可以设置prototype指向现存的对象来继承该对象。

对象的__proto__指向自己构造函数的prototype。obj.proto.proto...的原型链由此产生
two = new Object()中Object是构造函数,所以two.__proto__就是Object.prototype。ES规范定义对象字面量的原型就是Object.prototype。

我们知道JS是单继承的,Object.prototype是原型链的顶端,所有对象从它继承了包括toString等等方法和属性。

Object本身是构造函数,继承了Function.prototype;Function也是对象,继承了Object.prototype

问题9:对String做扩展,实现如下方式获取字符串中频率最高的字符

法1:遍历
String.prototype.getMostOften = function(){
            var arr = [];
            for(var i=0;i<this.length;i++){
                if(arr[this[i]]){
                    ++arr[this[i]];
                }else{
                    arr[this[i]] = 1;//必须在这里赋值为1
                }
            }

            var count = 0;
            var maxKey;
            for(var key in arr){
                if(arr[key]>count){
                    count = arr[key];//数字
                    maxKey = key;//a,b..
                }
            }
            var maxCount = count;
            return maxKey+':因为'+maxKey+'  出现了'+maxCount+'次';

        }
  var str = 'ahbbccdeddddfg';
  var ch = str.getMostOften();
  console.log(ch); //d , 因为d 出现了5次

检查元素__console.log

法2:正则
 String.prototype.getMostOften= function(str){
        return str.split('').sort().join('').match(/(\S)\1*/g).sort(function(a,b){
            return b.length - a.length;
        })[0][0];
};
    var str = 'ahbbccdeddddfg';
    var ch = str.getMostOften(str);
    var length = str.split('').sort().join('').match(/(\S)\1*/g).sort(function(a,b){
            return b.length - a.length;
        })[0].length;
    console.log(ch+':因为'+ch+'出现了'+length+'次'); //d , 因为d 出现了5次

sort() 方法用于对数组的元素进行排序,是按照字符编码的顺序进行排序
\S	匹配任何非空白字符
\1 指定第一个子匹配项
*	匹配前面的子表达式零次或多次

console.log

问题10: instanceOf有什么作用?内部逻辑是如何实现的?

  • instanceof用于判断某个变量是否是某个对象的实例,返回值为true或false

  • instanceof
    通过探测obj.proto.proto... === Constructor.prototype来验证obj是否是Constructor的实例。
    :对象的__proto__属性,一直往下查__proto__属性是不是等于构造函数的prototype属性

继承相关问题

问题11:继承有什么作用?

  • 继承是指一个对象直接使用另一对象的属性和方法。可以提高复用性,省代码
  • 作用:继承划分了类的层次性,父类代表的是更一般、更泛化的类,而子类则是更为具体、更为细化

问题12: 下面两种写法有什么区别?

//方法1
function People(name, sex){
    this.name = name;
    this.sex = sex;
    this.printName = function(){
        console.log(this.name);
    }
}
var p1 = new People('hunger', 2)

//方法2
function Person(name, sex){
    this.name = name;
    this.sex = sex;
}

Person.prototype.printName = function(){
    console.log(this.name);
}
var p1 = new Person('若愚', 27);

方法一

方法二

  • 方法一中:printName 方法直接写在函数Person中,当创建Person的实例时,每个实例都有自己的printName 方法方法二中:printName 方法是写在Person.prototype中,当创建Person的实例时,每个实例中没有自己的PrintName方法,当实例调用PrintName时,无法在自己体内找到,所以会通过__proto__去原型里面找,也就是Person.prototype当中去找。所以第二种方法中,PrintName是所有实例公用的。
  • 作用
    第二种在原型里面找方法:不用自己再开辟内存空间去创建一个方法

问题13: Object.create 有什么作用?兼容性如何?

  • 使用 Object.create 创建对象
    ECMAScript 5 中引入了一个新方法:Object.create()可以调用这个方法来创建一个新对象。新对象的原型就是调用 create
     方法时传入的第一个参数
  • 兼容性
    这是ES5才有的方法对于低版本浏览器兼容效果不理想

问题14: hasOwnProperty有什么作用? 如何使用?

  • 检测对象的属性是定义在自身上还是在原型链上,有必要使用** [hasOwnProperty] **方法,所有继承自 Object.proptotype 的对象都包含这个方法。会返回一个布尔值
    [hasOwnProperty]是 JavaScript 中唯一一个只涉及对象自身属性而不会遍历原型链的方法。

image.png

问题15:如下代码中call的作用是什么?

function Person(name, sex){
    this.name = name;
    this.sex = sex;
}
function Male(name, sex, age){
    Person.call(this, name, sex);   
    //这里的 call作用:改变作用域,可以引用构造函数,使Male实例可以访问PersonPerson的属性
    this.age = age;
}

问题16: 补全代码,实现继承

function Person(name, sex){
    this.name = name;
    this.sex = sex;
}
Person.prototype.getName = function(){
    console.log(this.name)
    console.log(this.age)
    console.log(this.sex)
};    
function Male(name, sex, age){
   Person.call(this,name,sex);
   this.age = age;
}

Male.prototype=Object.create(Person.prototype)
//or

// var Temp = function(){}
// Temp.prototype = Person.prototype;
// Male.prototype = new Temp();
// Male.prototype.construction = Male;

Male.prototype.getAge = function(){
    console.log(this.age)
};
var ruoyu = new Male('若愚', '男', 27);
ruoyu.getName();

进阶1----js文件位置+白屏+async+浏览器渲染机制

进阶1

CSS和JS在网页中的放置顺序是怎样的?

  • 为什么引入js文件?

因为网页 = Html+CSS+JavaScript;
Html: 网页元素内容
CSS: 控制网页样式
JavaScript:操作网页内容,实现功能或者效果.

  • css放在head标签里面
    方式:
  • link标签推荐使用
<head>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
  • <style type="text/css"> 样式 </style>
<head>
    <style>

    .content {
        background: red;
    }

    </style>
</head>
  • 引入外部 CSS 文件
<style>
    @import url(style.css);
</style>

参考

  • 引入js方式如下
    JS代码存放在标签对<script>...</script>中,而<script>...</script>放在body中.推荐放在闭合标签</body>之前
<body>
    <script type="text/javascript">
        //这里编写JavaScript程序
    </script>
</body>
  • 使用script标签的src属性引入一个js文件
    引用外部JS文件的script标签可以放在head标签内,也可以放在body标签中.
<script src="js/index.js" type="text/javascript"></script>
  • 元素事件中引入JS
<body>
    <input type="button" onClick="alert('绿叶学习网')" value="按钮"/>
</body>
  • 页头引入JS,指的就是在标签内编写JavaScript
<head>
    <title></title>
    <script type="text/javascript">
        //这里编写JavaScript程序
    </script>
</head>

解释白屏和FOUC?

  • 白屏产生原因:
    渲染引擎开始解析html,并将标签转化为内容树中的dom节点。接着,它解析外部CSS文件及style标签中的样式信息。这些样式信息以及html中的可见性指令将被用来构建另一棵树——render树。

  • render树:将 DOM 与 CSSOM 合并成一个渲染树.
    为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html都解析完成之后再去构建和布局render树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
    如果我们在Render树未完全绘制并渲染之前,向下快速拖动滚动条会看到上图所示的白屏现象,浏览器渲染不及时造成.

  • 场景:
    如果使用 @import标签,即使 CSS 放入 link, 并且放在头部,也可能出现白屏。
    如果把js文件放在头部,脚本会阻塞后面内容的呈现,脚本会阻塞其后组件的下载,出现白屏问题

  • FOUC:
    FOUC 全称 Flash of unstyled content,无样式内容闪烁.是指 HTML 页面在打开过程中,内容先于样式展示,导致页面样式在瞬间出现剧变,并且人眼可见
    1、使用@import方法导入CSS时,例如:
    <style type="text/css" media="all">@import "../fouc.css";</style>
    此方式由于IE会先加载整个HTML文档的DOM,然后再去导入外部的CSS文件,因此,在页面DOM加载完成到CSS导入完成中间会有一段时间页面上的内容是没有样式的,这段时间的长短跟网速,电脑速度都有关系。
    2、将样式表链接放在页面不同位置时,在IE5/6下某些页面会无样式显示内容且瞬间闪烁,这现象就是文档样式短暂失效(Flash Of Unstyled Content),即FOUC。

  • 关于FOUC,其实是FireFox的渲染逻辑和Chrome的不太一样,一开始Chrome是等待渲染树和位置计算好才会出来网页的内容,但到了FireFox每加载一次dom树就会重绘一次网页样式

  • **解决方法:**避免使用@import方法导入CSS,且将样式表链接都放在head中即可避免

async和defer的作用是什么?有什么区别?

  • <script src="script.js"></script>
    没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。

  • <script async src="script.js"></script>
    async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。

  • <script defer src="script.js"></script>
    defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。

  • 区别
    defer:脚本延迟到文档解析和显示后执行,有顺序
    async:不保证顺序

简述网页的渲染机制?

  • 处理 HTML 标记并构建 DOM 树。
    处理 CSS 标记并构建 CSSOM 树。
    将 DOM 与 CSSOM 合并成一个渲染树render 树。
    计算每个Frame(也就是每个Element)的位置,这又叫layout
    根据渲染树来布局,以计算每个节点的Frame(也就是每个Element)的位置,这又叫layout和reflow过程。
    最后通过调用操作系统Native GUI的API绘制,将各个节点绘制到屏幕上。

参考

js__Ajax__实践

题目 1: ajax 是什么?有什么作用?

  • Ajax是Asynchronous JavaScript and XML的缩写,这一技术能够向服务器请求额外的数据而无需卸载整个页面.

  • AJAX 在浏览器与 Web 服务器之间使用异步数据传输(HTTP 请求)从服务器获取数据,这里的异步是指脱离当前浏览器页面的请求、加载等单独执行,这意味着可以在不重新加载整个网页的情况下,通过JavaScript接受服务器传来的数据,然后操作DOM将新数据对网页的某部分进行更新,

  • Ajax是一种浏览器和服务器的通讯技术,Ajax不是浏览器自带功能

  • open()方法的第三个参数表示请求采用异步还是同步方式,默认值为true–异步方式

  • 作用
    使用Ajax最直观的感受是向服务器获取新数据不需要刷新页面等待了,良好的用户体验

题目 2: 前后端开发联调需要注意哪些事情?后端接口完成前如何 mock 数据?

  • 注意点:

  • 约定数据:需要传输的数据以及数据类型;

  • 约定接口:接口名称,请求和响应的格式,请求的参数名称,响应的数据格式;

  • 约定方式 :是get还是post,后端用同样的方式接收发送过来的数据

  • 前后端独立开发,前端向Mock Server发送请求,获取模拟的数据进行开发和测试

  • 如果接口修改了,Mock Server要同步修改

  • 如何 mock ?
    方法一:本地Mock Server做法:前端把Mock Server克隆到本地,开发的时候,开启前端工程服务器和Mock Server,所有的请求都发向本地服务器,获取到Mock数据.
    方法二: 使用nodejs的express框架

题目3:点击按钮,使用 ajax 获取数据,如何在数据到来之前防止重复点击?

  • 1.在请求数据方法前引入布尔变量如var isDataArrive = true,
    2.在绑定按钮点击请求数据事件的方法里的开头做次判断,如果发请求前有值(即isDataArrive = ture ),就return,不请求,因为在
    3.在xhr.onreadystatechange方法里的判断xhr.readystate的值===4的方法里引入布尔变量isDataArrive = true ,
    4.在发送请求的方法xhr.send()后引入布尔变量isDataArrive = false.
var check = true;
btn.addEventListener("click",function(){
    if(!check){
      return;
    }
    var xhr=new XMLHttpRequest()
    xhr.onreadystatechange=function(){
      if(xhr.readyState==4&&(xhr.status==200||xhr.status==304)){
          console.log(xhr.response);
          check = true;
      }   
    }
    xhr.open(method,url,true);
    xhr.send();
    check=false
})

题目4:封装一个 ajax 函数,能通过如下方式调用。后端在本地使用server-mock来 mock 数据

<script>
//封装的方法
function ajax(opts){
  var xhr = new XMLHttpRequest()
  xhr.onreadystatechange = function(){
      if(xhr.readyState === 4){
          if(xhr.status === 200 || xhr.status === 304){
              var results = JSON.parse(xhr.responseText)
              opts.success('results')
          }else{
              opts.error()
          }
      }
  }
  var query = '?'
  for (key in opts.data){
      query += key + '=' + opts.data[key] + '&'
      //query = query + key + '=' + opts.data[key] + '&'
      //?username=xiaoming& 遍历第一次
      //?username=xiaoming&password=abcd1234& 第二次
  }

  query = query.substr(0, query.length-1)//去除最后一位的&--->?username=xiaoming&password=abcd1234
  xhr.open(opts.type, opts.url+query, true)//配置参数,这里 opts.url+query = '/login?username=xiaoming&password=abcd1234'
  xhr.send()//发生请求
}

//点击事件
document.querySelector('#btn').addEventListener('click', function () {
    ajax({
        url: '/login',//接口地址 
        type: 'get', // 类型, post 或者 get, 
        data: {
            username: 'xiaoming',
            password: 'abcd1234'
        },
        success: function (ret) {
            console.log(ret); //ret为xhr.responseText,成功请求到的内容             
        },
        error: function () {
            console.log('出错了')
        }
    })
});

</script>




题目5:实现加载更多的功能,效果范例,后端在本地使用server-mock来模拟数据

  • 效果

使用Ajax加载更多

HTML__CSS部分

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>
    加载更多
  </title>
  <style>
    ul,
    li {
      margin: 0;
      padding: 0
    }

    #ct li {
      list-style: none;
      border: 1px solid #ccc;
      padding: 10px;
      margin-top: 10px;
      cursor: pointer;
    }

    #load-more {
      display: block;
      margin: 10px auto;
      text-align: center;
      cursor: pointer;
    }

    .btn {
      display: inline-block;
      height: 40px;
      line-height: 40px;
      width: 80px;
      border: 1px solid #E27272;
      border-radius: 3px;
      text-align: center;
      text-decoration: none;
      color: #E27272;
    }

    .btn:hover {
      background: green;
      color: #fff;
    }

    #ct>li:hover {
      background-color: pink;
    }
  </style>
</head>

<body>
  <ul id="ct">
  </ul>
  <a id="load-more" class="btn" href="#">
      加载更多
    </a>
</body>
</html>

js部分

<script>
  var ct = document.querySelector('#ct')
  var btn = document.querySelector('#load-more')

  var curIndex = 0  //当前要加载的数据的序号
  var len = 5   // 每次加载多少个数据
  var isLoading = false  //状态锁,用于判断是否在加载数据


  //点击事件
  btn.addEventListener('click', function (e) {
    e.preventDefault();  //防止点击 a 链接页面跳到顶部

    if (isLoading) {
      return   //如果正在请求数据,那这次点击什么都不做
    }

    //执行到这里说明 没有正在发出的请求,那后面就可以发请求
    ajax('/loadMore', {
      idx: curIndex,
      len: len
    }, function (data) {
      appendData(data)
      isLoading = false   //数据到来之后 解
      curIndex = curIndex + len  //修改序号,下次要数据就从新序号开始要
      console.log(curIndex)
    })
    isLoading = true   //发请求之前做个标记加锁

  })


  //封装的函数
  function ajax(url, json, onSuccess, onError) {
    var xhr = new XMLHttpRequest()
    var arr = []
    for (key in json) {
      arr.push(key + '=' + json[key])
    }
    url += '?' + arr.join('&')
    xhr.open('get', url)
    xhr.send()

    xhr.onload = function () {//若xhr请求成功,就会触发xhr.onreadystatechange和xhr.onload两个事件。 那么我们到底要将成功回调注册在哪个事件中呢?我倾向于 xhr.onload事件,因为xhr.onreadystatechange是每次xhr.readyState变化时都会触发,而不是xhr.readyState=4时才触发
      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
        onSuccess(JSON.parse(this.response))//this = xhr对象   this.response 是 responseText
      } else {
        onError && onError()
      }
    }
  }



  //封装函数,data 为 JSON.parse(this.response)即响应内容
  function appendData(data) {
    for (var i = 0; i < data.length; i++) {
      var child = document.createElement('li')
      child.innerText = data[i]
      ct.appendChild(child)
    }
  }

  function onError() {
    console.log('出错了')
  }

</script>

后端部分:router.js

app.get('/loadMore', function (req, res) {

	var curIdx = req.query.idx//通过query去拿,这里的idx对应前端js发生请求的参数idx
	var len = req.query.len
	var data = []

	for (var i = 0; i < len; i++) {
		data.push('头条' + (parseInt(curIdx) + i))
	}

	res.send(data);
});

说明

代码规范

命名统一使用小写

js-字符串+JSON

进阶5:字符串方法,数组的方法,字符串与数组相互转换,回文,JSON

1.使用数组拼接出如下字符串


var prod = {
    name: '女装',
    style: ['春季','冬季','夏季']
};
function getTpl(data){
  var arr = [];
  arr.push("<dl class=\"product\">\n");
  arr.push("\t<dt>" + data.name  +"</dt>\n");
  for(var key in data.style){
    arr.push("\t<dd>" + data.style[key] +"</dd>\n");
  }
  arr.push("</dl>");
  return arr.join("");
}
var result = getTpl(prod);
console.log(result); //result为下面的字符串
输出:
<dl class="product">
    <dt>女装</dt>
    <dd>短款</dd>
    <dd>冬季</dd>
    <dd>春装</dd>
</dl>

2.写出两种以上声明多行字符串的方法

  • 法一:在每一行的尾部使用反斜杠''
var longString = "Long \
long \
long \
string";
console.log(longString);//"Longlong long string"
  • 法二:连接运算符(+)可以连接多个单行字符串
var longString = 'Long'
  + 'long '
  + 'long '
  + 'string';
console.log(longString);//"Longlong long string"
  • 法三
(function () { /*
line 1
line 2
line 3
*/}).toString().split('\n').slice(1,-1).join('\n')
// "line 1 line 2 line 3"

3.补全如下代码,让输出结果为字符串: hello\饥人谷

var str = 'hello\\\\饥人谷';
console.log(str); //"hello\\饥人谷"

4.以下代码输出什么?为什么

var str = 'jirengu\nruoyu'
console.log(str.length);// 输出13,\n是转义字符代表换行只占据一个字符.'\'单独使用也不算一个字符.

5.写一个函数,判断一个字符串是回文字符串,如 abcdcba是回文字符串, abcdcbb不是.

function isReverse(str){
  return str == str.split('').reverse().join(''); //把字符串转为数组,并把数组翻转然后再连接成字符串
}
var str1 = "asdfdsa";
isReverse(str1);//ture

6.写一个函数,统计字符串里出现出现频率最多的字符

function fn(str) {
    var dict = {};
    for (var i = 0; i < str.length; i++) {
        if (dict[str[i]]) {
            ++dict[str[i]];
        } else {
            dict[str[i]] = 1;
        }

    }

    var count = 0;
    var maxValue;
    for (key in dict) {
        if (dict[key] > count) {
            maxValue = key;
            count = dict[key];

        }
    }
    console.log("maxValue is:", maxValue, "count:", count);

}
var str1 = "afggjf";
fn(str1);

7.写一个camelize函数,把my-short-string形式的字符串转化成myShortString形式的字符串,如

camelize("background-color") == 'backgroundColor'
camelize("list-style-image") == 'listStyleImage'

  var text = "background-color";
  var text2 = "list-style-image";

  function camelize(str){//以 "background-color为例
  var arr = str.split('-');//以-切割数组:[background,color]
  var arr3 = arr.slice(0,1);//切割后的第一个元素放到一个数组:[background]
  var arr2 = arr.slice(1);//切割后的除了第一个元素之后的数组:[color]
  var newArr = [];//用来保存第一个字母是大写的数组,即第二个数组.
  for(var i = 0; i < arr2.length; i++){
    
    newArr.push(arr2[i][0].toUpperCase() + arr2[i].slice(1));//[Color]
  } 
  var arr4 = (arr3.concat(newArr)).join(""); //把第一个没被大写的数组和
    //被改为大写的数组进行拼接.:[backgroundColor]
    
    return arr4;
}
    console.log(camelize(text));
    console.log(camelize(text2));

8.写一个 ucFirst函数,返回第一个字母为大写的字符

ucFirst("hunger") == "Hunger"
简单法:
function fn(str){
  var s = str[0].toUpperCase();
  var b = str.substr(1);
  return s + b;
}
console.log(fn("asd"));
复杂法:
 function ucFirst(str){
  var arr = str.split('');//[a,s,d]
  var arr1 = arr.slice(0,1);//[a]
  var arr2 = arr.slice(1);//[sd]

  var newArr = [];
  newArr.push(arr1[0].toUpperCase() + arr2);//newArr=["As,d"]

  var str2 = newArr.join().replace(/,/g,'');//替换全局的','为空格
    console.log(str2);

}
   ucFirst("asmd");

9.写一个函数truncate(str, maxlength), 如果str的长度大于maxlength,会把str截断到maxlength长,并加上...,如

truncate("hello, this is hunger valley,", 10) == "hello, thi...";
truncate("hello world", 20) == "hello world"


function fn(str,max){
  if(str.length>max){
    var arr1 = str.split('');
   arr1.splice(max,str.length-max,'...');//splice()是在第i个的前面插入,删除第i个后面. 
  }
  console.log(arr1.join(''));//这里可以直接取,
}

str1 = "javascript";
fn(str1,5);//截取前5位字符,并加...

10.什么是 json?什么是 json 语言?JSON 语言如何表示对象?window.JSON 是什么?

  • 什么是 JSON?
    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于JavaScript的一个子集。

  • 什么是 JSON 语言?
    JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成(网络传输速度)。

  • JSON 语言如何表示对象?
    JSON 语法是 JavaScript 对象表示语法的子集.
    数据在名称/值对中
    数据由逗号分隔.
    花括号保存对象
    方括号保存数组.
    JSON 数据的书写格式是:名称/值对,名称/值对组合中的名称写在前面(在双引号中),值对写在后面(同样在双引号中),中间用冒号隔开:

var json1 = {"name": "Byron", "age": "24"}

var json2 = [
    {"name": "Byron", "age": "24"}, 
    {"name": "Byron2", "age": "25"}
]
  • window.JSON 是什么?
    window.JSON是浏览器内置对象.
    其中JSON.parse()表示把字符串解析为JSON对象;
    而JSON.stringify()表示将JSON对象解析为字符串.

11.如何把JSON 格式的字符串转换为 JS 对象?如何把 JS对象转换为 JSON 格式的字符串?

//将对象转换为JSON格式的字符串
var obj = {
    "name" : "luoqian",
    "age" : 21
}
JSON.stringify(obj);

//JSON格式的字符串转换为对象
var str = '{"name":"luoqian","age":21}';
JSON.parse(str);

js__闭包__定时器__BOM

1.闭包是面试必问题,考察代码输出结果和使用场景 2.定时器JavaScript提供定时执行代码的功能 3.BOM 是指浏览器对象模型,BOM提供了独立于内容的、可以与浏览器窗口进行互动的对象结构

题目1: 下面的代码输出多少?修改代码让 fnArr[i]() 输出 i.使用 两种以上的方法

   var fnArr = [];
    for (var i = 0; i < 10; i ++) {
        fnArr[i] =  function(){
    	    return i;
        };
    }
    console.log( fnArr[3]() );  //
  • 输出:10
//方法1
var fnArr = [];
for(var i=0;i<10;i++){
  (function(i){
    fnArr[i] = function(){
    return i;
  }
  })(i)
}
console.log(fnArr[3]());
//方法2
var fnArr = [];
for(var i=0;i<10;i++){
  fnArr[i] = (function(n){
    return function(){
        return n;
    }
  })(i)
}
console.log(fnArr[3]());
总结:
1.在函数内部再return一个函数,但不传参数,让最里面的函数调用外部的参数.
2.立即执行外部函数,返回值并赋值.

题目2: 封装一个汽车对象,可以通过如下方式获取汽车状态

var Car = (function(){
   var speed = 0;
   function setSpeed(s){
       speed = s;
   }
   function getSpeed(){
      return  speed;
   }
   function accelerate(){
     return speed +=10;
   }
  function decelerate(){
     return  speed>0 ? speed -=10 : speed = 0;
  }
  function getStatus(){
    return speed>0 ? 'running' :'stop';
  }
   return {
      'setSpeed': setSpeed,
      'getSpeed': getSpeed,
      'accelerate': accelerate,
     'decelerate': decelerate,
     'getStatus': getStatus
   };
})();
Car.setSpeed(30);
console.log( Car.getSpeed() ); //30
console.log( Car.accelerate() );
console.log( Car.getSpeed() ); //40;
console.log( Car.decelerate() );
console.log( Car.decelerate() );
console.log( Car.getSpeed() ); //20
console.log( Car.getStatus() ); // 'running';
console.log( Car.decelerate() ); 
console.log( Car.decelerate() );
console.log( Car.getStatus() );  //'stop';

题目3:下面这段代码输出结果是? 为什么?

var a = 1;
setTimeout(function(){
    a = 2;
    console.log(a);
}, 0);
var a ;
console.log(a);
a = 3;
console.log(a);

输出:
'1'
'3'
'2'
why:  setTimeout方法要在js代码全部执行完后再调用

题目4:下面这段代码输出结果是? 为什么?

var flag = true;
setTimeout(function(){
    flag = false;
},0)
while(flag){}//setTimeout中的函数会等待前面执行完毕,而这里是死循环
console.log(flag);//不执行

:陷入死循环


题目5: 下面这段代码输出?如何输出delayer: 0,

// delayer:1...(使用闭包来实现)

法1:
    for(var i=0;i<5;i++){
      (function(){ 
        var n = i;             
      setTimeout(function(){
      console.log('delayer:' + n );
      }, 0)
      })()
      console.log(i);
    }


法2:
    for (var i = 0; i < 5; i++) {
        (function (t) {
            return setTimeout(function () {
                console.log('delayer:' + t);
            }, 0);
        })(i);
        console.log(i);
    }

题目6: 如何获取元素的真实宽高

window.getComputedStyle(node).width
window.getComputedStyle(node).height
//低版本IE
window.currentStyle(node).width
window.currentStyle(node).width

题目7: URL 如何编码解码?为什么要编码?

编码:
encodeURI()
encodeURIComponent()
解码:
decodeURI()
decodeURIComponent()

-----
如果你需要编码整个URL,然后需要使用这个URL,那么用encodeURI.
当你需要编码URL中的参数的时候,那么encodeURIComponent是最好方法

  • 为什么要编码?
  • 编码是为了避免解析网址时产生歧义
    Http协议中参数的传输是"key=value"这种简直对形式的,如果要传多个参数就需要用“&”符号对键值对进行分割。如"?name1=value1&name2=value2",这样在服务端在收到这种字符串的时候,会用“&”分割出每一个参数,然后再用“=”来分割出参数值。
    这样一个问题,如果我的参数值中就包含=或&这种特殊字符的时候该怎么办。
    比如说“name1=value1”,其中value1的值是“va&lu=e1”字符串,那么实际在传输过程中就会变成这样“name1=va&lu=e1”。我们的本意是就只有一个键值对,但是服务端会解析成两个键值对,这样就产生了歧义.
    解决的办法就是对参数进行URL编码

题目8: 补全如下函数,判断用户的浏览器类型

function isAndroid(){
    return /android/i.test(window.navigator.userAgent);
}
function isIphone(){
    return /iphone/i.test(window.navigator.userAgent);
}
function isIpad(){
    return /ipad/i.test(window.navigator.userAgent);
}
function isIOS(){
    return /ipad|iphone/i.test(window.navigator.userAgent);
}

prototype_constructor _proto_ instanceof_Object.create()

�关键字:
1.构造函数
2.prototype的使用
3.原型链
4. constructor
5.Object.create()
6. Object.prototype._ proto _
7.获取原型对象方法的比较
写作原因:理清原型的诸多概念

1.什么是构造函数?

构造函数形如👇:
function Cat (name, color) {
 this.name = name;
 this.color = color;
}

var cat1 = new Cat('花花', '黑色');

cat1.name // '花花'
cat1.color // '黑色'

构造函数的缺点:
这样做是对系统资源的浪费,因为同一个构造函数的对象实例之间,无法共享属性.

2.为什么有prototype这个属性?

JavaScript 的每个对象都继承另一个对象,后者称为“原型”(prototype)对象。只有null除外,它没有自己的原型对象。

原型对象上的所有属性和方法,都能被派生对象共享。这就是 JavaScript 继承机制的基本设计。

通过构造函数生成实例对象时,会自动为实例对象分配原型对象。每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。

function Animal (name) {
  this.name = name;
}

Animal.prototype.color = '黑色';

var cat1 = new Animal('花花');
var cat2 = new Animal('喵喵');

cat1.color // '黑色'
cat2.color // '黑色'

上面代码中,构造函数Animal的prototype对象,就是实例对象cat1和cat2的原型对象。在原型对象上添加一个color属性。结果,实例对象都能读取该属性。

原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。
当实例对象本身没有某个属性或方法的时候,它会到构造函数的prototype属性指向的对象,去寻找该属性或方法。这就是原型对象的特殊之处。
如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。
总结一下:原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。

Animal.prototype.walk = function () {
  console.log(this.name + ' is walking');
};

上面代码中,Animal.prototype对象上面定义了一个walk方法,这个方法将可以在所有Animal实例对象上面调用。

由于 JavaScript 的所有对象都有构造函数(只有null除外),而所有构造函数都有prototype属性(其实是所有函数都有prototype属性),所以所有对象都有自己的原型对象。

3.原型链是什么?有什么作用?

对象的属性和方法,有可能是定义在自身,也有可能是定义在它的原型对象。由于原型本身也是对象,又有自己的原型,所以形成了一条原型链(prototype chain)。比如,a对象是b对象的原型,b对象是c对象的原型,以此类推。

如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性指向的那个对象。那么,Object.prototype对象有没有它的原型呢?回答可以是有的,就是没有任何属性和方法的null对象,而null对象没有自己的原型。

Object.getPrototypeOf(Object.prototype)// null

上面代码表示,Object.prototype对象的原型是null,由于null没有任何属性,所以原型链到此为止。

“原型链”的作用是:
读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。

如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。

需要注意的是,一级级向上,在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。

下面的代码可以找出,某个属性到底是原型链上哪个对象自身的属性。

function getDefiningObject(obj, propKey) {
  while (obj && !{}.hasOwnProperty.call(obj, propKey)) {
    obj = Object.getPrototypeOf(obj);
  }
  return obj;
}
//对象实例的hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。
Date.hasOwnProperty('length')// true

4.constructor

prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。

原型指向图

function P() {}
P.prototype.constructor === P// true

由于constructor属性定义在prototype对象上面,意味着可以被所有实例对象继承。

function P() {}
var p = new P();

p.constructor
// function P() {}

p.constructor === P.prototype.constructor
// true

p.hasOwnProperty('constructor')
// false

上面代码中,p是构造函数P的实例对象,但是p自身没有contructor属性,该属性其实是读取原型链上面的P.prototype.constructor属性。

constructor属性的作用,是分辨原型对象到底属于哪个构造函数。

function F() {};
var f = new F();
f.constructor === F // true
f.constructor === RegExp // false
上面代码表示,使用constructor属性,确定实例对象f的构造函数是F,而不是RegExp。

有了constructor属性,就可以从实例新建另一个实例。

function Constr() {}
var x = new Constr();

var y = new x.constructor();
y instanceof Constr // true
上面代码中,x是构造函数Constr的实例,可以从x.constructor间接调用构造函数。

这使得在实例方法中,调用自身的构造函数成为可能。

Constr.prototype.createCopy = function () {
  return new this.constructor();
};

由于constructor属性是一种原型对象与构造函数的关联关系,所以修改原型对象的时候,务必要小心。

function A() {}
var a = new A();
a instanceof A // true

function B() {}
A.prototype = B.prototype;
a instanceof A // false
上面代码中,a是A的实例。修改了A.prototype以后,constructor属性的指向就变了,导致instanceof运算符失真。
instanceof运算符用来比较一个对象是否为某个构造函数的实例

所以,修改原型对象时,一般要同时校正constructor属性的指向。

//推荐写法
C.prototype.method1 = function (...) { ... };

instanceof运算符返回一个布尔值,表示指定对象是否为某个构造函数的实例。

var v = new Vehicle();
v instanceof Vehicle // true
上面代码中,对象v是构造函数Vehicle的实例,所以返回true。

instanceof运算符的左边是实例对象,右边是构造函数。它会检查右边构建函数的原型对象,是否在左边对象的原型链上。

由于instanceof对整个原型链上的对象都有效,因此同一个实例对象,可能会对多个构造函数都返回true

var d = new Date();
d instanceof Date // true
d instanceof Object // true
上面代码中,d同时是Date和Object的实例,因此对这两个构造函数都返回true。

除了上面这种继承null的特殊情况,JavaScript 之中,只要是对象,就有对应的构造函数。因此,instanceof运算符的一个用处,是判断值的类型。
instanceof运算符只能用于对象,不适用原始类型的值。

此外,对于undefined和null,instanceOf运算符总是返回false。
undefined instanceof Object // false
null instanceof Object // false

利用instanceof运算符,还可以巧妙地解决,调用构造函数时,忘了加new命令的问题。

function Fubar (foo, bar) {
  if (this instanceof Fubar) {
    this._foo = foo;
    this._bar = bar;
  }
  else {
    return new Fubar(foo, bar);
  }
}
上面代码使用instanceof运算符,在函数体内部判断this关键字是否为构造函数Fubar的实例。如果不是,就表明忘了加new命令。

5.Object.create()

生成实例对象的常用方法,就是使用new命令,让构造函数返回一个实例。但是很多时候,只能拿到一个实例对象,它可能根本不是由构建函数生成的,那么能不能从一个实例对象,生成另一个实例对象呢?

JavaScript 提供了Object.create方法,用来满足这种需求。该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承继承原型对象的属性。

// 原型对象
var A = {
print: function () {
console.log('hello');
}
};

// 实例对象
var B = Object.create(A);
B.print() // hello
B.print === A.print // true
上面代码中,Object.create方法以A对象为原型,生成了B对象。B继承了A的所有属性和方法。

6.Object.prototype._ proto _

_ proto _ 属性(前后各两个下划线)可以改写某个对象的原型对象。

var obj = {};
var p = {};

obj._ proto _ = p;
Object.getPrototypeOf(obj) === p // true
上面代码通过_ proto _ 属性,将p对象设为obj对象的原型。

根据语言标准,_ proto _ 属性只有浏览器才需要部署,其他环境可以没有这个属性,而且前后的两根下划线,表示它本质是一个内部属性,不应该对使用者暴露。因此,应该尽量少用这个属性,而是用Object.getPrototypeof()(读取)和Object.setPrototypeOf()(设置),进行原型对象的读写操作。

7.获取原型对象方法的比较

_ proto _ 属性指向当前对象的原型对象,即构造函数的prototype属性。

var obj = new Object();

obj._ proto _ === Object.prototype
// true
obj._ proto _ === obj.constructor.prototype
// true
上面代码首先新建了一个对象obj,它的_ proto _ 属性,指向构造函数(Object或obj.constructor)的prototype属性。所以,两者比较以后,返回true。

因此,获取实例对象obj的原型对象,有三种方法。

obj._ _proto_ _
obj.constructor.prototype
Object.getPrototypeOf(obj)

上面三种方法之中,前两种都不是很可靠。最新的ES6标准规定,_ proto _ 属性只有浏览器才需要部署,其他环境可以不部署。而obj.constructor.prototype在手动改变原型对象时,可能会失效。
推荐使用第三种Object.getPrototypeOf方法,获取原型对象。


参考阮一峰的ES5高级教程

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.