js&jq面试笔记,该部分为下部分。

字符串相关

1、定义一个方法,用于将string中的每个字符之间加一个空格,并输出

如:'hello' -> 'h e l l o'

function joinSpace(str){
    return str.split('').join(' ')
}

2、JavaScript中如何检测一个变量是一个String类型?请写出函数实现

方法1
function isString(obj){
    return typeof(obj) === "string"? true: false;
    // returntypeof obj === "string"? true: false;
}

方法2
function isString(obj){
    return obj.constructor === String? true: false;
}

方法3
function isString(obj){
     return Object.prototype.toString.call(obj) === "[object String]"?true:false;
}
如:
var isstring = isString('xiaoming');
console.log(isstring);  // true

3、请用js去除字符串空格?

方法一:使用replace正则匹配的方法
    去除所有空格: 
    str = str.replace(/\s*/g,"");      
    
    去除两头空格: 
    str = str.replace(/^\s*|\s*$/g,"");
    
    去除左空格: 
    str = str.replace( /^\s*/, “”);
    
    去除右空格: 
    str = str.replace(/(\s*$)/g, "");
    
    str为要去除空格的字符串,实例如下:
    var str = " 23 23 ";
    var str2 = str.replace(/\s*/g,"");
    console.log(str2); // 2323

方法二:使用str.trim()方法
    str.trim() //局限性:无法去除中间的空格,实例如下:
    var str = "   xiao  ming   ";
    var str2 = str.trim();
    console.log(str2);   //xiao  ming 
    同理:
    str.trimLeft(),str.trimRight() //分别用于去除字符串左右空格。

方法三:使用jquery,$.trim(str)方法
    $.trim(str) 局限性:无法去除中间的空格,实例如下:
    var str = "   xiao  ming   ";
    var str2 = $.trim(str)
    console.log(str2);   //  xiao  ming

4、你如何获取浏览器URL中查询字符串中的参数?

//测试地址为:http://www.runoob.com/jquery/misc-trim.html?channelid=12333&name=xiaoming&age=23
function showWindowHref(){
    var sHref = window.location.href;
    var args = sHref.split('?');
    if(args[0] == sHref){
        return "";
    }
    var arr = args[1].split('&');
    var obj = {};
    for(var i = 0;i< arr.length;i++){
        var arg = arr[i].split('=');
        obj[arg[0]] = arg[1];
    }
    return obj;
}
var href = showWindowHref(); // obj
console.log(href['name']); // xiaoming

5、js 字符串操作函数

//这里只是列举了常用的字符串函数,具体使用方法,请参考网址。
concat()        将两个或多个字符的文本组合起来,返回一个新的字符串。
indexOf()       返回字符串中一个子串第一处出现的索引。如果没有匹配项,返回 -1 。
charAt()        返回指定位置的字符。
lastIndexOf()   返回字符串中一个子串最后一处出现的索引,如果没有匹配项,返回 -1 。
match()         检查一个字符串是否匹配一个正则表达式。
substr()        返回从string的startPos位置,长度为length的字符串
substring()     返回字符串的一个子串。传入参数是起始位置和结束位置。
slice()         提取字符串的一部分,并返回一个新字符串。
replace()       用来查找匹配一个正则表达式的字符串,然后使用新字符串代替匹配的字符串。
search()        执行一个正则表达式匹配查找。如果查找成功,返回字符串中匹配的索引值。否则返回 -1 。
split()         通过将字符串划分成子串,将一个字符串做成一个字符串数组。
length          返回字符串的长度,所谓字符串的长度是指其包含的字符的个数。
toLowerCase()   将整个字符串转成小写字母。
toUpperCase()   将整个字符串转成大写字母。

6、判断一个字符串中出现次数最多的字符,统计这个次数

var str = 'asdfssaaasasasasaa';
var json = {};
for (var i = 0; i < str.length; i++) {
    if(!json[str.charAt(i)]){   //判断json中是否存在当前str.charAr(i)的值
       json[str.charAt(i)] = 1;   //如果不存在,则将其存放在json中,并且赋值为1,相当于出现的次数为1
    }else{
       json[str.charAt(i)]++;  //如果存在,则这个字符的值加1,相当于次数加1
    }
};
var iMax = 0;
var iIndex = '';
for(var i in json){
    if(json[i]>iMax){   //判断当前json中的键值(相当于当前键所在字符的次数)是否大于iMax
         iMax = json[i];
         iIndex = i;
    }
}        
console.log('出现次数最多的是:'+iIndex+'出现'+iMax+'次');

数组(Array)对象

1、Array相关的属性和方法

  • Array 对象属性
constructor 返回对创建此对象的数组函数的引用。

var test=new Array();
if (test.constructor==Array){
    document.write("This is an Array");
}
    
length 设置或返回数组中元素的数目。

prototype 使您有能力向对象添加属性和方法。
  • Array 对象方法
join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
    var arr = ['xiao','lin','qiqi','mingtian'];
    var arr2 = arr.join(',');
    console.log(arr2); // 根据','隔开返回的字符串为:"xiao,lin,qiqi,mingtian"
    
pop() 删除并返回数组的最后一个元素。 
    var arr = [2,3,4,5];
    var arr2 = arr.pop();
    console.log(arr2); // 删除的数组的最后一个元素为:5
    console.log(arr);  // 删除元素之后的数组为:[2, 3, 4]
    
shift() 删除并返回数组的第一个元素
    var arr = [2,3,4,5];
    var arr2 = arr.shift();
    console.log(arr2); // 删除的数组的第一个元素为:2
    console.log(arr);  // 删除元素之后的数组为:[3, 4,5]
    
push() 向数组的末尾添加一个或更多元素,并返回新的长度。
    var arr = [2,3,4,5];
    var arr2 = arr.push(6);
    console.log(arr2);  // 返回的数组长度:5 
    console.log(arr);  // [2, 3, 4, 5, 6]
    
unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
    var arr = ['xiao','ming','qiqi','aiming'];
    var arr1 = arr.unshift('lang');
    console.log(arr1);  // 返回的数组的长度:  5
    console.log(arr);  //向数组开头添加元素返回的结果:["lang", "xiao", "ming", "qiqi", "aiming"]
    
reverse() 颠倒数组中元素的顺序。
    var arr = [2,3,4,5];
    arr.reverse();
    console.log(arr);   //  [5, 4, 3, 2]
    
slice() 从某个已有的数组返回选定的元素
    var arr = [2,3,4,5];
    var arr2 = arr.slice(1,3);
    console.log(arr2);  // 截取区间返回的数组为:[3, 4]
    console.log(arr);  // [2, 3, 4, 5]
    
sort() 对数组的元素进行排序
    借助排序函数,实现数值由小到大排序
    function sortNumber(a,b){
        return a - b
    }
    var arr = [23,30,42,5];
    var arr2 = arr.sort(sortNumber);
    console.log(arr2);  // [5, 23, 30, 42]
    console.log(arr);   // [5, 23, 30, 42]

    借助排序函数,实现数值由大到小排序
    function sortNumber(a,b){
        return b - a
    }
    var arr = [23,30,42,5];
    var arr2 = arr.sort(sortNumber);
    console.log(arr2);  // [42, 30, 23, 5]
    console.log(arr);  // [42, 30, 23, 5]

splice() 删除元素,并向数组添加新元素。
    语法:arrayObject.splice(index,howmany,item1,.....,itemX)
    index:必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
    howmany:必需。要删除的项目数量。如果设置为 0,则不会删除项目。
    item1, ..., itemX:可选。向数组添加的新项目。

    // 创建一个新数组,并向其添加一个元素
    var arr = [1,2,3,4];
    arr.splice(2,0,5);
    console.log(arr);  // [1, 2, 5, 3, 4]

    // 删除位于 index 2 的元素,并添加一个新元素来替代被删除的元素:
    var arr = [1,2,3,4];
    arr.splice(2,1,5);
    console.log(arr);  // [1, 2, 5, 4]
    
toString() 把数组转换为字符串,并返回结果。
    var arr = ['xiao','ming','qiqi','aiming'];
    arr.toString();
    console.log(arr);  // ["xiao", "ming", "qiqi", "aiming"]

2、编写一个方法 去掉一个数组的重复元素

方法一
var arr = [0,2,3,4,4,0,2];
var obj = {};
var tmp = [];
for(var i = 0 ;i< arr.length;i++){
   if( !obj[arr[i]] ){
      obj[arr[i]] = 1;
      tmp.push(arr[i]);
   }
}
console.log(tmp);       //结果如下: [0, 2, 3, 4]

方法二
var arr = [2,3,4,4,5,2,3,6];
var arr2 = [];
for(var i = 0;i< arr.length;i++){
    if(arr2.indexOf(arr[i]) < 0){
        arr2.push(arr[i]);
    }
}
console.log(arr2);      //结果为:[2, 3, 4, 5, 6]

方法三
var arr = [2,3,4,4,5,2,3,6];
var arr2 = arr.filter(function(element,index,self){
    return self.indexOf(element) === index;
});
console.log(arr2);      //结果为:[2, 3, 4, 5, 6]

方法四:(ES6提供了新的数据结构 Set)
let unique= [...new Set(array)];
let unique= [...new Set([2,3,4,4,5,2,3,6])];
console.log(unique)

3、求数组的最值?

求数组最大值:Math.max.apply(null,arr);
var arr = [3,43,23,45,65,90];
var max = Math.max.apply(null,arr);
console.log(max);
// 90

求数组最小值:Math.min.apply(null,arr);
var arr = [3,43,23,45,65,90];
var min = Math.min.apply(null,arr);
console.log(min);
// 3

4、数组排序相关

选择排序
/**
*以一个角标的元素和其他元素进行比较。
*在内循环第一次结束,最值出现的头角标位置上。
*/
function selectSort(array) {
    if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
        var len = array.length, temp;
        for (var i = 0; i < len - 1; i++) {  //轮数
            var min = array[i];
            for (var j = i + 1; j < len; j++) {  //第一个与第二个比较
                if (array[j] < array[i]) {
                    temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }
            }
        }
        return array;
    } else {
        return 'array is not an Array!';
    }
}

冒泡排序
/**
*比较方式:相邻两个元素进行比较,如果后一个比前一个小,换位置。
*原理:内循环结束一次,最值出现在尾角标位置。
*/
function bubbleSort(array) {
    if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
        var len = array.length, temp;
        for(var j=0;j<len-1;j++){
        //两两比较,如果前一个比后一个大,则交换位置。
           for(var i=0;i<len-1-j;i++){  //-i:让每次参与比较的元素减少。-1:避免角标越界。
                if(arr[i]>arr[i+1]){
                    var temp = arr[i];
                    arr[i] = arr[i+1];
                    arr[i+1] = temp;
                }
            } 
        }
        return array;
    } else {
        return 'array is not an Array!';
    }
}


二分插入排序
/**
*为了提高查找效率,可使用折半查找的方式,注意:这种查找只对有序的数组有效。这种方式也叫二分查找法。
*/
function binaryInsertionSort(array) {
    if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
        for (var i = 1; i < array.length; i++) {
            var key = array[i], left = 0, right = i - 1;
            while (left <= right) {
                var middle = parseInt((left + right) / 2);
                if (key < array[middle]) {
                    right = middle - 1;
                } else {
                    left = middle + 1;
                }
            }
            for (var j = i - 1; j >= left; j--) {
                array[j + 1] = array[j];
            }
            array[left] = key;
        }
        return array;
    } else {
        return 'array is not an Array!';
    }
}

5、数组的翻转(非reverse())

方法一:
var arr = [1,2,3,4];
var arr2 = [];
while(arr.length) {
    var num = arr.pop(); //删除数组最后一个元素并返回被删除的元素
    arr2.push(num);
}
console.log(arr2);
// [4, 3, 2, 1]

方法二:
var arr = [1,2,3,4];
var arr2 = [];
while(arr.length){
    var num = arr.shift(); //删除数组第一个元素并返回被删除的元素
    arr2.unshift(num);
}
console.log(arr2);

6、在Javascript中什么是伪数组?如何将伪数组转化为标准数组?

  • 伪数组(类数组):无法直接调用数组方法或期望length属性有什么特殊的行为,但仍可以用真正数组遍历方法来遍历它们。

典型的是函数的argument参数,还有像调用getElementsByTagName,document.childNodes之类的,它们都返回NodeList对象都属于伪数组。

a.使用Array.prototype.slice.call();        
    Array.prototype.slice.call({  
     0:"likeke",  
     1:12,  
     2:true,  
     length:3  
    });  
    //["likeke", 12, true]
    
b.使用[].slice.call(),了解js原型链的都知道,实际上这种方法和第一中方法是一样的,但上面第一种方式相对效率更高。
    [].slice.call({  
     0:"likeke",  
     1:12,  
     2:true,  
     length:3  
    });  
    //["likeke", 12, true] 
    
c. 使用ES6中Array.from方法;
    Array.from({  
     0:"lk",  
     1:12,  
     2:2013,  
     3:"长安大学",  
     length:4  
    });  
    //["lk", 12, 2013, "长安大学"]

对象相关

1、获取JavaScript对象键名

var obj = {name: 'wan',age: 20, sex: 'male'};  
var objKey = [];  
for(var item in obj){  
  if(obj.hasOwnProperty(item)){  
    objKey.push(item);  
  }  
}  
//for(var item in obj){  
//    objKey.push(item);  
//}  
console.log(objKey);//["name", "age", "sex"]

2、call和apply的区别

  • 语法一:func.call(thisObj,a,b,c...) || func.apply(thisObj,array)定义:将函数(方法) func 绑定到对象 thisObj 上去运行,改变了函数func 原有的 this 的指向,this指向了对象thisObj(js中的函数是对象,函数名是对 Function 对象的引用)
  • 语法二:obj.call(thisObj,a,b,c...) || obj.apply(thisObj,array)定义:将对象 obj 的属性和方法添加到对象 thisObj 上,即 thisObj 继承了 obj
  • 区别:call的参数个数,从第二个开始 大于等于 0个,为单个变量;apply的参数只有两个,第二个参数为一个数组,即array = [a,b,c...];

实例一:借调方法,对象sub借调对象add的方法add(函数也是对象)

    function add(a,b){return a+b}

    function sub(a,b){return a-b}

    add.call(sub,3,1)//4

    sub.call(add,3,1)//2

    add.call(sub,3,1) === add(3,1)

实例二:改变this指向

    function a(){console.log(this)}

    var obj = {}

    a()//window

    a.call()//window

    a.call(obj)//obj

实例三:实现继承

    function Animal(name){this.name=name;this.showName=function(){alert(this.name)}}
    function Cat(name){

        Animal.call(this,name); //将Animal对象的属性和方法应用到Cat上,因此Cat继承了Animal的所有属性和方法
    }
    var cat = new Cat(“Black Cat”);
    cat.showName(); //Black Cat

实例四:多重继承


    function add(a,b){return a+b}

    function sub(a,b){return a-b}

    function calc(a,b){

        add.call(this)

        sub.call(this)

    }

3、js中改变this指向的方法

call、apply、bind,this总是指向调用它的对象

4、为下面的类增加一个方法method1

var A = function(){}
A.prototype.method1 = function(a,b){return a+b}

5、实现Parent类和Child类,并建立Parent和Child的继承关系

  • 面向对象的基本特征有:封闭、继承、多态。
function Parent(){
    this.surname = 'wan';
    this.work = function(){console.log('i like work')}
}
function Child(){}

原型链继承:Child.prototype = new Parent()
    实例化:let person = new Child();
    console.log(person.surname);//wan
    person.work();//i like work

构造函数继承:function Child(){Parent.call(this)}

组合继承:
    function Child(){
        Parent.call(this);//继承属性
    }
    Child.prototype = new Parent();//继承方法

原型式继承:
    function object(obj){
      function Func(){};
      Func.prototype = obj;
      return new Func();
    }
    var Child = object(Parent);

寄生式继承:

寄生组合式继承:

6、解释原型和原型链

  • 原型:即原型对象,原型对象上定义方法和属性的目的是为了被子类继承和使用。原型链的形成真正是靠__proto__ 而非prototype
  • 原型链:每个对象都有原型,对象的原型指向原型对象,即子对象的原型指向父对象,父对象的原型指向爷爷对象,这种原型层层连接起来的就构成了原型链。

7、定义一个类的私有属性和公有属性

function Person(){
    var sex = 'man';//var 私有
    this.surnname = 'wan';//this 公有
}

8、实现一个函数clone,可以对JavaScript中的5种主要的数据类型(包括Number、String、Object、Array、Boolean)进行值复制。

(Object.prototype.toString.call()方法及应用)
/**
 * 对象克隆
 * 支持基本数据类型及对象
 * 递归方法
 */
function clone(obj) {
    var o;
    switch (typeof obj) {
        case "undefined":
            break;
        case "string":
            o = obj + "";
            break;
        case "number":
            o = obj - 0;
            break;
        case "boolean":
            o = obj;
            break;
        case "object": // object 分为两种情况 对象(Object)或数组(Array)
            if (obj === null) {
                o = null;
            } else {
                if (Object.prototype.toString.call(obj).slice(8, -1) === "Array") {
                    o = [];
                    for (var i = 0; i < obj.length; i++) {
                        o.push(clone(obj[i]));
                    }
                } else {
                    o = {};
                    for (var k in obj) {
                        o[k] = clone(obj[k]);
                    }
                }
            }
            break;
        default:
            o = obj;
            break;
    }
    return o;
}

//测试
var a=[12,34,'123'];
console.log(clone(a));
var b=null;
console.log(clone(b));
var c={1:'a',2:'b',3:'c'};
console.log(clone(c));
var d=1;
console.log(clone(d));

9、创建对象

  • 使用Object构造函数或对象字面量都可以创建对象,但缺点是创建多个对象时,会产生大量的重复代码,因此下面介绍可解决这个问题的创建对象的方法
1、工厂模式
    function createPerson(name, age, job) {
        var o = new Object();
        o.name = name;
        o.age = age;
        o.job = job;
        o.getName = function () {
            return this.name;
        }
        return o;//使用return返回生成的对象实例
    }
    var person = createPerson('Jack', 19, 'SoftWare Engineer');
    创建对象交给一个工厂方法来实现,可以传递参数,但主要缺点是无法识别对象类型,因为创建对象都是使用Object的原生构造函数来完成的。

2、构造函数模式
    function Person(name,age,job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.getName = function () {
            return this.name;
        }
    }
    var person1 = new Person('Jack', 19, 'SoftWare Engineer');
    var person2 = new Person('Liye', 23, 'Mechanical Engineer');
    使用自定义的构造函数(与普通函数一样,只是用它来创建对象),定义对象类型(如:Person)的属性和方法。它与工厂方法区别在于:

    没有显式地创建对象,直接将属性和方法赋值给this对象;没有return语句;
    此外,要创建Person的实例,必须使用new关键字,以Person函数为构造函数,传递参数完成对象创建;实际创建经过以下4个过程:

    创建一个对象
    将函数的作用域赋给新对象(因此this指向这个新对象,如:person1)
    执行构造函数的代码
    返回该对象
    上述由Person构造函数生成的两个对象person1与person2都是Person的实例,因此可以使用instanceof判断,并且因为所有对象都继承Object,因此person1 instanceof Object也返回真:

    alert(person1 instanceof Person);//true;
    alert(person2 instanceof Person);//true;
    alert(person1 instanceof Object);//true;
    alert(person1.constructor === person2.constructor);//ture;
    虽然构造函数方式比较不错,但也存在缺点,那就是在创建对象时,特别针对对象的属性指向函数时,会重复的创建函数实例,以上述代码为基础,可以改写为:

    function Person(name,age,job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.getName = new Function () {//改写后效果与原代码相同,不过是为了方便理解
            return this.name;
        }
    }
    上述代码,创建多个实例时,会重复调用new Function();创建多个函数实例,这些函数实例还不是一个作用域中,当然这一般不会有错,但这会造成内存浪费。
    当然,可以在函数中定义一个getName = getName的引用,而getName函数在Person外定义,这样可以解决重复创建函数实例问题,但在效果上并没有起到封装的效果,如下所示:

    function Person(name,age,job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.getName = getName;
    }
    function getName() {//到处是代码,看着乱!!
        return this.name;
    }
    
    
3、原型模式
    JS每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,它是所有通过new操作符使用函数创建的实例的原型对象。
    原型对象最大特点是,所有对象实例共享它所包含的属性和方法,也就是说,所有在原型对象中创建的属性或方法都直接被所有对象实例共享。

    function Person(){}
    Person.prototype.name = 'Jack';//使用原型来添加属性
    Person.prototype.age = 29;
    Person.prototype.getName = function(){
        return this.name;
    }
    var person1 = new Person();
    alert(person1.getName());//Jack
    var person2 = new Person();
    alert(person1.getName === person2.getName);//true;共享一个原型对象的方法

    原型是指向原型对象的,这个原型对象与构造函数没有太大关系,唯一的关系是函数的prototype是指向这个原型对象!而基于构造函数创建的对象实例也包含一个内部指针为:[[prototype]]指向原型对象。

实例属性或方法的访问过程是一次搜索过程:
    首先从对象实例本身开始,如果找到属性就直接返回该属性值;
    如果实例本身不存在要查找属性,就继续搜索指针指向的原型对象,在其中查找给定名字的属性,如果有就返回;
    基于以上分析,原型模式创建的对象实例,其属性是共享原型对象的;但也可以自己实例中再进行定义,在查找时,就不从原型对象获取,而是根据搜索原则,得到本实例的返回;简单来说,就是实例中属性会屏蔽原型对象中的属性;

原型与in操作符
    一句话:无论原型中属性,还是对象实例的属性,都可以使用in操作符访问到;要想判断是否是实例本身的属性可以使用object.hasOwnProperty(‘attr’)来判断;

原生对象中原型
    原生对象中原型与普通对象的原型一样,可以添加/修改属性或方法,如以下代码为所有字符串对象添加去左右空白原型方法:
    
    String.prototype.trim = function(){
        return this.replace(/^\s+/,'').replace(/\s+$/,'');
    }
    var str = ' word space ';
    alert('!'+str.trim()+'!');//!word space!
    
    原型模式的缺点,它省略了为构造函数传递初始化参数,这在一定程序带来不便;另外,最主要是当对象的属性是引用类型时,它的值是不变的,总是引用同一个外部对象,所有实例对该对象的操作都会其它实例:

    function Person() {    }
    Person.prototype.name = 'Jack';
    Person.prototype.lessons = ['Math','Physics'];
    var person1 = new Person();
    person1.lessons.push('Biology');
    var person2 = new Person();
    alert(person2.lessons);//Math,Physics,Biology,person1修改影响了person2

    
4、组合构造函数及原型模式
    目前最为常用的定义类型方式,是组合构造函数模式与原型模式。构造函数模式用于定义实例的属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方方法的引用,最大限度的节约内存。此外,组合模式还支持向构造函数传递参数,可谓是集两家之所长。

    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.lessons = ['Math', 'Physics'];
    }
    Person.prototype = {
        constructor: Person,//原型字面量方式会将对象的constructor变为Object,此外强制指回Person
        getName: function () {
            return this.name;
        }
    }
    var person1 = new Person('Jack', 19, 'SoftWare Engneer');
    person1.lessons.push('Biology');
    var person2 = new Person('Lily', 39, 'Mechanical Engneer');
    alert(person1.lessons);//Math,Physics,Biology
    alert(person2.lessons);//Math,Physics
    alert(person1.getName === person2.getName);//true,//共享原型中定义方法

    在所接触的JS库中,jQuery类型的封装就是使用组合模式来实例的!!!


5、动态原型模式
    组合模式中实例属性与共享方法(由原型定义)是分离的,这与纯面向对象语言不太一致;动态原型模式将所有构造信息都封装在构造函数中,又保持了组合的优点。

    其原理就是通过判断构造函数的原型中是否已经定义了共享的方法或属性,如果没有则定义,否则不再执行定义过程。该方式只原型上方法或属性只定义一次,且将所有构造过程都封装在构造函数中,对原型所做的修改能立即体现所有实例中:

    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.lessons = ['Math', 'Physics'];
    }
    if (typeof this.getName != 'function') {//通过判断实例封装
      Person.prototype = {
        constructor: Person,//原型字面量方式会将对象的constructor变为Object,此外强制指回Person
        getName: function () {
          return this.name;
        }
      }
    }
    var person1 = new Person('Jack', 19, 'SoftWare Engneer');
    person1.lessons.push('Biology');
    var person2 = new Person('Lily', 39, 'Mechanical Engneer');
    alert(person1.lessons);//Math,Physics,Biology
    alert(person2.lessons);//Math,Physics
    alert(person1.getName === person2.getName);//true,//共享原型中定义方法

jQuery相关

1、jQuery为DOM元素绑定点击事件的方法和区别

.click(function(){}) 

    .bind({'click mouseleave',function(){}},{'click mouseleave',function(){}}) //在.bind()绑定事件的时候,这些元素必须已经存在。

    .on() //为动态绑定事件

    .one("click", function() {alert("This will be displayed only once.");});//绑定一个事件,并且只运行一次,然后删除自己,

2、jQuery 库中的 $() 是什么?

  $() 函数是 jQuery() 函数的别称。$() 函数用于将任何对象包裹成 jQuery 对象,接着你就被允许调用定义在 jQuery 对象上的多个不同方法。

可以将一个选择器字符串传入` $()` 函数,它会返回一个包含所有匹配的 DOM 元素数组的 jQuery 对象。

3、如何找到所有 HTML select 标签的选中项?

$('[name=selectname] :selected')

4、$(this)this 关键字在 jQuery 中有何不同?

  • $(this) 返回一个 jQuery 对象,你可以对它调用多个 jQuery 方法,比如用 text() 获取文本,用val() 获取值等等。而 this 代表当前元素,它是 JavaScript 关键词中的一个,表示上下文中的当前 DOM 元素。你不能对它调用 jQuery 方法,直到它被 $() 函数包裹,例如 $(this)。

5、jquery怎么移除标签onclick属性?

获得a标签的onclick属性: $("a").attr("onclick")

删除onclick属性:$("a").removeAttr("onclick");

设置onclick属性:$("a").attr("onclick","test();");

6、jquery中addClass,removeClass,toggleClass的使用。

$(selector).addClass(class):为每个匹配的元素添加指定的类名

$(selector).removeClass(class):从所有匹配的元素中删除全部或者指定的类,删除class中某个值;

$(selector).toggleClass(class):如果存在(不存在)就删除(添加)一个类

$(selector).removeAttr(class);删除class这个属性;

7、JQuery有几种选择器?

(1)、基本选择器:#id,class,element,*;

(2)、层次选择器:parent > child,prev + next ,prev ~ siblings

(3)、基本过滤器选择器::first,:last ,:not ,:even ,:odd ,:eq ,:gt ,:lt

(4)、内容过滤器选择器: :contains ,:empty ,:has ,:parent

(5)、可见性过滤器选择器::hidden ,:visible

(6)、属性过滤器选择器:[attribute] ,[attribute=value] ,[attribute!=value] ,[attribute^=value] ,[attribute$=value] ,[attribute*=value]

(7)、子元素过滤器选择器::nth-child ,:first-child ,:last-child ,:only-child

(8)、表单选择器: :input ,:text ,:password ,:radio ,:checkbox ,:submit 等;

(9)、表单过滤器选择器::enabled ,:disabled ,:checked ,:selected

8、jQuery中的Delegate()函数有什么作用?

  • delegate()会在以下两个情况下使用到:
    1、如果你有一个父元素,需要给其下的子元素添加事件,这时你可以使用delegate()了,代码如下:
`$("ul").delegate("li", "click", function(){ $(this).hide(); });`

2、当元素在当前页面中不可用时,可以使用delegate()

9、$(document).ready()方法和window.onload有什么区别?

(1)、window.onload 方法是在网页中所有的元素(包括元素的所有关联文件)完全加载到浏览器后才执行的。

(2)、$(document).ready() 方法可以在DOM载入就绪时就对其进行操纵,并调用执行绑定的函数。

10、如何用jQuery禁用浏览器的前进后退按钮?

$(document).ready(function() {
    window.history.forward(1);
    //OR window.history.forward(-1);
  });

11、 jquery中$.get()提交和$.post()提交有区别吗?

  • 相同点:都是异步请求的方式来获取服务端的数据;
  • 异同点:1、请求方式不同:$.get() 方法使用GET方法来进行异步请求的。$.post() 方法使用POST方法来进行异步请求的。
    2、参数传递方式不同:get请求会将参数跟在URL后进行传递,而POST请求则是作为HTTP消息的实体内容发送给Web服务器的,这种传递是对用户不可见的。
    3、数据传输大小不同:get方式传输的数据大小不能超过2KB 而POST要大的多
    4、安全问题: GET 方式请求的数据会被浏览器缓存起来,因此有安全问题。

12、写出一个简单的$.ajax()的请求方式?

$.ajax({
    url:'http://www.baidu.com',
    type:'POST',
    data:data,
    cache:true,
    headers:{},
    beforeSend:function(){},
    success:function(){},
    error:function(){},
    complete:function(){}
});

13、jQuery的事件委托方法bind 、live、delegate、on之间有什么区别?

  • (1)、bind 【jQuery 1.3之前】
定义和用法:主要用于给选择到的元素上绑定特定事件类型的监听函数;

语法:bind(type,[data],function(eventObject));

特点:

  (1)、适用于页面元素静态绑定。只能给调用它的时候已经存在的元素绑定事件,不能给未来新增的元素绑定事件。

  (2)、当页面加载完的时候,你才可以进行bind(),所以可能产生效率问题。

实例如下:$( "#members li a" ).bind( "click", function( e ) {} );
  • (2)、live 【jQuery 1.3之后】
定义和用法:主要用于给选择到的元素上绑定特定事件类型的监听函数;

语法:live(type, [data], fn);

特点:

  (1)、live方法并没有将监听器绑定到自己(this)身上,而是绑定到了this.context上了。

  (2)、live正是利用了事件委托机制来完成事件的监听处理,把节点的处理委托给了document,新添加的元素不必再绑定一次监听器。

  (3)、使用live()方法但却只能放在直接选择的元素后面,不能在层级比较深,连缀的DOM遍历方法后面使用,即$(“ul”").live...可以,但$("body").find("ul").live...不行; 

实例如下:$( document ).live( "click", "#members li a", function( e ) {} );
  • (3)、delegate 【jQuery 1.4.2中引入】
定义和用法:将监听事件绑定在就近的父级元素上

语法:delegate(selector,type,[data],fn)

特点:

  (1)、选择就近的父级元素,因为事件可以更快的冒泡上去,能够在第一时间进行处理。

  (2)、更精确的小范围使用事件代理,性能优于.live()。可以用在动态添加的元素上。

实例如下:

$("#info_table").delegate("td","click",function(){/*显示更多信息*/});

$("table").find("#info").delegate("td","click",function(){/*显示更多信息*/});
  • (4)、on 【1.7版本整合了之前的三种方式的新事件绑定机制】
定义和用法:将监听事件绑定到指定元素上。

语法:on(type,[selector],[data],fn)

实例如下:$("#info_table").on("click","td",function(){/*显示更多信息*/});参数的位置写法与delegate不一样。

说明:on方法是当前JQuery推荐使用的事件绑定方法,附加只运行一次就删除函数的方法是one()。

总结:.bind(), .live(), .delegate(),.on()分别对应的相反事件为:.unbind(),.die(), .undelegate(),.off()

开发及性能优化

1、规避javascript多人开发函数重名问题

命名空间
封闭空间
js模块化mvc(数据层、表现层、控制层)
seajs
变量转换成对象的属性
对象化

2、请说出三种减低页面加载时间的方法

压缩css、js文件
合并js、css文件,减少http请求
外部js、css文件放在最底下
减少dom操作,尽可能用变量替代不必要的dom操作

3、你所了解到的Web攻击技术

  • (1)XSS(Cross-Site Scripting,跨站脚本攻击):指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或者JavaScript进行的一种攻击。
  • (2)SQL注入攻击
  • (3)CSRF(Cross-Site Request Forgeries,跨站点请求伪造):指攻击者通过设置好的陷阱,强制对已完成的认证用户进行非预期的个人信息或设定信息等某些状态更新。

4、web前端开发,如何提高页面性能优化?

  • 内容方面:
1.减少 HTTP 请求 (Make Fewer HTTP Requests)

2.减少 DOM 元素数量 (Reduce the Number of DOM Elements)

3.使得 Ajax 可缓存 (Make Ajax Cacheable)
  • 针对CSS:
1.把 CSS 放到代码页上端 (Put Stylesheets at the Top)

2.从页面中剥离 JavaScript 与 CSS (Make JavaScript and CSS External)

3.精简 JavaScript 与 CSS (Minify JavaScript and CSS)

4.避免 CSS 表达式 (Avoid CSS Expressions)
  • 针对JavaScript :
1. 脚本放到 HTML 代码页底部 (Put Scripts at the Bottom)

2. 从页面中剥离 JavaScript 与 CSS (Make JavaScript and CSS External)

3. 精简 JavaScript 与 CSS (Minify JavaScript and CSS)

4. 移除重复脚本 (Remove Duplicate Scripts)
  • 面向图片(Image):
1.优化图片

2 不要在 HTML 中使用缩放图片

3 使用恰当的图片格式

4 使用 CSS Sprites 技巧对图片优化

5、前端开发中,如何优化图像?图像格式的区别?

  • 优化图像:
1、不用图片,尽量用css3代替。 比如说要实现修饰效果,如半透明、边框、圆角、阴影、渐变等,在当前主流浏览器中都可以用CSS达成。

2、 使用矢量图SVG替代位图。对于绝大多数图案、图标等,矢量图更小,且可缩放而无需生成多套图。现在主流浏览器都支持SVG了,所以可放心使用!

3.、使用恰当的图片格式。我们常见的图片格式有JPEG、GIF、PNG。

基本上,内容图片多为照片之类的,适用于JPEG。

而修饰图片通常更适合用无损压缩的PNG。

GIF基本上除了GIF动画外不要使用。且动画的话,也更建议用video元素和视频格式,或用SVG动画取代。

4、按照HTTP协议设置合理的缓存。

5、使用字体图标webfont、CSS Sprites等。

6、用CSS或JavaScript实现预加载。

7、WebP图片格式能给前端带来的优化。WebP支持无损、有损压缩,动态、静态图片,压缩比率优于GIF、JPEG、JPEG2000、PG等格式,非常适合用于网络等图片传输。
  • 图像格式的区别:
矢量图:图标字体,如 font-awesome;svg 

位图:gif,jpg(jpeg),png
  • 区别:
1 、gif:是是一种无损,8位图片格式。具有支持动画,索引透明,压缩等特性。适用于做色彩简单(色调少)的图片,如logo,各种小图标icons等。

2、JPEG格式是一种大小与质量相平衡的压缩图片格式。适用于允许轻微失真的色彩丰富的照片,不适合做色彩简单(色调少)的图片,如logo,各种小图标icons等。

3、png:PNG可以细分为三种格式:PNG8,PNG24,PNG32。后面的数字代表这种PNG格式最多可以索引和存储的颜色值。

关于透明:PNG8支持索引透明和alpha透明;PNG24不支持透明;而PNG32在24位的PNG基础上增加了8位(256阶)的alpha通道透明;
  • 优缺点:
1、能在保证最不失真的情况下尽可能压缩图像文件的大小。

2、对于需要高保真的较复杂的图像,PNG虽然能无损压缩,但图片文件较大,不适合应用在Web页面上。

ES5和ES6

1、ES5和ES6的类

ES5的类
function Person(name) {
    this.name = name; 
}

Person.prototype.sayHello = function(){
    return 'Hi, I am ' + this.name;
}

ES6的类
class Person {
    constructor(name){
        this.name = name;
    }
    sayHello(){
        return 'Hi, I am ' + this.name;
    } 
}

typeof Person; //'function'

调用的方式都是一致的:
var me = new Person('Yecao');

2、ES5的继承和ES6的继承有什么区别?

  • ES5的继承时通过prototype或构造函数机制来实现。ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.apply(this))。
  • ES6的继承机制完全不同,实质上是先创建父类的实例对象this(所以必须先调用父类的super()方法),然后再用子类的构造函数修改this。
ES6继承
class Father{
    constructor(name){
        this.name = name;
    }
    getName(){
        console.log(this.name);
    }
    //  这里是父类的f方法
    f(){
        console.log('fffffffffffffffffffffff');
    }
}

class Son extends Father{
    constructor(name,age){
        super(name); // HACK: 这里super()要在第一行
        this.age = age;
    }
    getAge(){
        console.log(this.age);
    }
    //  子类的f方法
    f(){
        console.log('sssssssssssssssssssssss');
    }
}
var s1 = new Son('张一',12);
s1.getName();
s1.getAge();
console.log(s1.__proto__);          //  为Son,不用修正
s1.f();                             //  打印ssssssssssssss
s1.__proto__ = new Father();        //  改变s1的原型指向,改为Father
s1.f();                             // 打印ffffffffffffff
console.log(s1.__proto__);          // 为Father

ES5继承
function Father(name){
  this.name = name;
}
function Son(name,age){
  Father.call(this,name);
  this.age = age;
}
Father.prototype.getName = function(){
  console.log(this.name);
}
// 这里注意原型继承要在,实例化s1变量之前,如果要使用原型链上的方法的话,子类的原型是父类的一个实例
Son.prototype = new Father;

// 修正构造器,这里注意要将Son的构造器指向赋值为Son,否则,打印出来的s1是Father对象
Son.prototype.constructor = Son;
Son.prototype.getAge = function(){
  console.log(this.age);
}
var s1 = new Son('李四',22);
console.log(s1);                            // Son {name:'李四',age:22}
s1.getName();                               // 李四
console.log(Son.prototype.constructor);     // Son
console.log(s1.constructor);                // Son,如果不纠正,则为Father
s1.getAge();                                // 22

//HACK:这里通过__proto__这个s1实例的属性找到了Son的prototype,并为其添加了say的方法
s1.__proto__.say = function(){  
  console.log('hhhhhhhhhhhhhhhhhhhhhhhh');
}
s1.say()                                    // 打印 hhhhhhhhhhhhhhh

// NOTE: __proto__这个属性是具体到某个实例化后的对象才有的属性,指向他所属的类的原型
console.log(new Son().__proto__);           // 为Son对象

2、ES6 Set和Map

Map和Set都叫做集合,但是他们也有所不同。Set常被用来检查对象中是否存在某个键名,Map集合常被用来获取已存的信息。

  • 1、set类似于数组,但是成员的值都是唯一的,没有重复的值。
    Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。
四个操作方法。
 add(value):添加某个值,返回Set结构本身。
 delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
 has(value):返回一个布尔值,表示该值是否为Set的成员。
 clear():清除所有成员,没有返回值。


Set 结构的实例有四个遍历方法,可以用于遍历成员。
keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
  • 2、Set类似于数组,而Map就类似于键值对(Key, Value);

它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
Map有size()属性,查看Map对象大小,set(key , value) , get(Key), delete(key) , has(key) ,clear()方法。

Map 结构原生提供三个遍历器生成函数和一个遍历方法。
 keys():返回键名的遍历器。
 values():返回键值的遍历器。
 entries():返回所有成员的遍历器。
 forEach():遍历 Map 的所有成员。