Javascript高级程序设计笔记,很重要尤其是对象的设计模式与继承

var obj = {'a':'a'};
var fun = function (){}
console.log(typeof obj);//object
console.log(typeof fun);//function
var a;
console.log(a === undefined);//true
console.log(typeof b);//true 未声明的变量使用会报错,但是他的typeof=undefined
var c=null;
console.log(typeof c);//object null是对象的空指针,如果声明变量将要保存的数据类型是object,那么初始化该变量的时候设置为null
console.log(null == undefined);//true undefined的值派生与null
var message = 'hello word';
var messageBoolean = Boolean(message);
console.log(messageBoolean);//true  把变量转换成boolean值
var num1 = 070,//56   0~7
    num2 = 0xA;//10   0~9 A~F 
console.log(num1);//56 八进制数值 
console.log(num2);//10 十进制数值
var sum = 0.1+0.2;
console.log(sum);//0.30000000000000004
var maxNum = Number.MAX_VALUE;
console.log(maxNum);//js 在一般浏览器中所能存储的最大值
var minNum = Number.MIN_VALUE;
console.log(minNum);//js 在一般浏览器中所能存储的最小值
var test = "this is the letter singma: \u03a3.";
console.log(test);

//object
var o = new Object();
o.name = 'tome';
console.log(o.hasOwnProperty('name'));//检查name属性是否存在o对象中
var numa = 10,//怎么正确理解前置操作和后置操作:1、操作的是变量;2、表达式运算    前置操作是先改变变量的值然后再进行表达式运算,后置操作是先进行表达式运算然后再改变变量的值;谨记变量的值在后期的运算中已经改变
    numb = 15;
var numc = ++numa + numb--;
console.log(numa);//11
console.log(numb);//14
console.log(numc);//26
console.log(numa+numb);//25
(function(){ console.log('this is a function');})();//匿名函数的调用方式
var somevar = false;//根据该值判断逻辑与的短路操作,逻辑或也是短路操作
var result = (somevar && undefinedvar);
console.log(result);
var n = 5+'5';
console.log(n);//55
console.log('--------------------------------');
for(var proName in window){//for(var name in object){} 循环对象中的属性
//    console.log(proName);
}
var num = 0;
outermost://label 语句在循环中使用continue和break时判断退出到哪层循环,正常情况下break只会退出一层循环
for (var i=0; i<10; i++){
    for (var j=0; j<10; j++){
        if(i == 5 && j == 5){
            break outermost;
        }
        //console.log(i+'---'+j);
    }
}
function sayHi(){
    console.log(arguments);//在函数内部中使用arguments获取参数的数组  arguments.length获取参数的个数
}
sayHi('a');
//函数的参数可以传object
function setName(obj){
    obj.name = "Nicholas"
}
var person = new Object();
setName(person);
console.log(person.name);
console.log(person instanceof Object);//person是对象吗
console.log(person instanceof Array);//person是数组吗
console.log(person instanceof RegExp);//person是正则表达式吗

function add(n1,n2){
    summ = n1 + n2;//有var会报错,因为var声明的变量是在当前作用域链下,如果没有var则是全局变量隶属于window(不声明直接赋值的做法不可取)
    return summ ;
}
add(1,2);
console.log(summ);//如果有var声明summ会报错
console.log(window.summ);//如果没用var声明变量则可以在window对象中找到该属性
var color = new Array(3);
var color = new Array('three');
console.log(color);//注意区分输出结果
//在数组后面追加元素
var color = ['red','blue','green'];
color[color.length] = 'black';
console.log(color);//["red", "blue", "green", "black"]
console.log(color instanceof Array);//true 数组检测
console.log(Array.isArray(color));//true 数组检测
console.log(color.toString());//数组转换成字符串,以逗号分隔
console.log(color.join("||"));//red||blue||green||black
//对象中定义成员方法
var people = {
    p:function(){
        return 'abc';
    }
}
console.log(people.p());
//数组排序
var values = [0,1,5,10,15];
console.log(values.sort(compare));
function compare(value1,value2){
    /*if(value1 < value2){
        return -1;
    }else if(value1 > value2){
        return 1;
    }else{
        return 0;
    }或者*/
    return value1 - value2; 
}
//数组迭代方法
var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item,index,array){//判断数组中的元素是否都大于2(数组判断)   
    return (item > 2);
});
console.log(everyResult);//false
var someResult = numbers.some(function(item,index,array){//判断数组中的元素是否有大于2的 (数组判断)       
    return (item >2 );
})
console.log(someResult);//true
var filterResult = numbers.filter(function(item,index,array){//返回数组中大于2的元素(数组过滤)
    return (item > 2);
});
console.log(filterResult);//[3, 4, 5, 4, 3]
var mapResult = numbers.map(function(item,index,array){//数组中的每一项都乘以2(数组运算)
    return (item * 2);
});
console.log(mapResult);//[2, 4, 6, 8, 10, 8, 6, 4, 2]
numbers.forEach(function(item,index,array){//数组的遍历     value/key/array
    console.log(item);
});
console.log('-----------');
var values = [1,2,3,4,5];
var sum = values.reduce(function(prev,cur,index,array){
    return prev + cur;
});
console.log(sum);//数组求和
//正则表达式
var text = "cat, bat, sat, fat";
var pattern1 = /.at/g;//g 注意g对matches的影响
console.log(pattern1.test(text));//true 在text中匹配pattern1
var matches = pattern1.exec(text);
console.log(matches);
/**
 * 函数
 */
function func(num1,num2){
    console.log(num1 + num2);
}
anotherFun = func;//函数名就是函数对象的指针(注意有括号和无括号的区别)  
anotherFun(10,10);
if(true){//函数作为参数传递
    function callSomeFunction(someFunction,someArguments){
        return someFunction(someArguments);
    }
    function add10(num){
        return num + 10;
    }
    console.log(callSomeFunction(add10,10));//20 
}
//数组中按照对象属性的值进行排序
function createComarisonFunction(propertyName){
    return function(object1,object2){//sort函数会自动传递过来两个参数
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if(value1 < value2){
            return -1;
        }else if(value1 > value2){
            return 1;
        }else{
            return 0;
        }
    }
}
var data = [{name:'Zachary',age:28},{name:'Nicholas',age:29}];
data.sort(createComarisonFunction("name"));
console.log(data);//[{name:'Nicholas',age:29},{name:'Zachary',age:28}]
data.sort(createComarisonFunction('age'));
console.log(data);//[{name:'Zachary',age:28},{name:'Nicholas',age:29}]
//函数内部属性this:全局对象,arguments:参数数组,arguments.callee:该函数,arguments.callee.caller:该函数的引用(也就是哪个函数中调用了这个函数)
function factorial(num){
    console.log(arguments.callee);//arguments.callee 指向该函数
}
factorial();

function outer(){
    inner();
}
function inner(){
    console.log(arguments.callee.caller);//function outer(){inner();}显示当前函数的引用函数(也就是哪个函数引用了这个函数)
}
outer();
//函数外部属性length:参数的个数,prototype
function sayName(name){
    return '';
}
console.log(sayName.length);//1 函数参数的个数
console.log(sayName.prototype);//空对象 toString()和valueOf()的方法实际上都保存在prototype名下
//函数方法:call,apply,bind方法         指定函数运行的作用域(谨记作用域链的概念)
window.color = 'red';
var o = {color:'blue'};
function sayColor(){
    return this.color;
}
console.log(sayColor.call(window));//red   在window环境下运行sayColor方法
console.log(sayColor.call(o));//blue    在o对象环境下运行sayColor方法 
var objectSayColor = sayColor.bind(o);//把sayColor绑定到o对象里面
console.log(objectSayColor());//blue

var s1 = "some text";
var s2 = s1.substring();
console.log(s2);

var num = 10;
console.log(num.toFixed(2));//10.00
var text = "cat, bat, sat, fat";
var parttern = /.at/;
console.log(text.match(parttern));
/**
 * 对象 object
 */
//对象中属性的特性:数据属性和访问器属性,数据属性包含(configurable:属性是否可删除  enumerable:属性是否可以用for-in遍历  writable:属性是否可以重写  value:属性设置的值),访问器属性包含get和set方法,访问器属性的常见用法是在设置一个属性的时候会让其他属性也发生变化
//数据属性
var person = {};
Object.defineProperty(person,"name",{//如果这样去设置对象中的属性,那么未设置的特性均为false
    writable:false,//该属性的值不可以修改(只读)
    enumerable:false,//是否可以使用for in遍历属性
    configurable:true,//是否可以删除属性,是否可以把数据属性修改为访问器属性
    value:'Nicholas'//设置值
});
console.log(person.name);//Nicholas
person.name = "Greg";//设置无效,在严格模式下会报错
console.log(person.name);//Nicholas
//访问器属性:通过设置对象的一个属性的值,会导致其他属性发生变化,这就是访问器属性的常用方法
var book = {
    _year:2004,
    edition:1
};
Object.defineProperty(book,"year",{
    get:function(){
        return this._year;
    },
    set:function(newValue){
        if(newValue > 2004){
            this._year = newValue;
            this.edition += newValue - 2004; 
        }
    }
});
book.year = 2005;//设置year的值会改变其他两个属性的值
console.log(book.edition);
//同时设置数据属性和访问器属性
var book = {};
Object.defineProperties(book,{
    _year:{//数据属性
        writable:true,
        value:2004
    },
    edition:{//数据属性
        writable:true,
        value:1
    },
    year:{
        get:function(){
            return this._year;
        },
        set:function(newValue){
            if(newValue > 2004){
                this._year = newValue;
                this.edition += newValue - 2004;
            }
        }
    }
});
book.year = 2005;//设置year的值会改变其他两个属性的值
console.log(book);
//读取属性的特性
var descriptor = Object.getOwnPropertyDescriptor(book,"_year");//读取_year属性的特性
console.log(descriptor.value);//2005
console.log(descriptor.configurable);//false 由于_year属性的configurable特性未设置,因此返回false
/**
 * 对象设计模式
 */
//1、构造函数模式(工厂模式实用性比较差)
/* function Person(name,age,job){//构造函数
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        return this.name;
    }
}
var person1 = new Person('Nicholas',29,"Software Engineer");//该步骤经历四个过程:1、person1 = {};2、Person.call(person1);构造函数的作用域赋值到对象3、执行函数中的代码;4、返回新对象
var person2 = new Person("Greg",27,"Doctor");
console.log(person1.name);//Nicholas
console.log(person2.name);//Greg
console.log(person1.sayName());//Nicholas */
/**总结**/
// 可以把构造函数看做是一个类,但是构造函数有一个缺点,就是包含方法的构造函数每实例化一个构造函数就会创建一个方法,即便是实现同样功能的方法也会在实例化构造函数的时候创建多个,可以通过以下方式来解决.如:
/* function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}
function sayName(){
    return this.name;
}
var person1 = new Person("Nicholas",29,"Softwar Engineer");
console.log(person1.sayName());//Nicholas */
//以上方法可以达到在实例化构造函数的时候不会创建多个函数(方法),但是这样就失去了封装性的意义

//2、原型模式:优点属性共享 缺点也是属性共享 因此就出现了
/* function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    return this.name;
}
var person1 = new Person();
console.log(person1.name);
console.log(Person.prototype.isPrototypeOf(person1));//true person1是Person的实例吗
console.log(Object.getPrototypeOf(person1));//Person.prototype  获取person1构造函数的原型对象
console.log(person1.hasOwnProperty("name"));//false   person1中是否有name属性,Person.prototype中的name属性不算
console.log("name" in person1);//true name属性是在person1实例中吗? 无论name属性是实例属性还是原型属性都可以取得到
person1.name = "Greg";
console.log("name" in person1);//ture name属性是实例中的属性
console.log(person.hasOwnProperty("name"));//ture
function hasPrototypeProperty(obj,name){//判断属性是否是原型中的属性
    return (name in obj) && !obj.hasOwnProperty(name);
}
console.log(hasPrototypeProperty(person1,"name"));//false  因为name属性不是原型属性而是实例属性
delete person1.name;
console.log(hasPrototypeProperty(person1,"name"));//true  此刻的那么属性是原型中的属性
//枚举属性(遍历属性)
var o = {
    toString:function(){
        return "My Object";
    },
    name:"Nicholas"
};
for(var prop in o){//遍历实例对象中的所有属性
    console.log(o[prop]);
}
for(var pro in Person.prototype){//遍历原型对象中的所有属性
    console.log(Person.prototype[pro]);
}
console.log(Object.keys(Person.prototype));//获取原型对象中的所有属性
console.log(Object.keys(person1));//获取实例对象中的所有属性
console.log(Object.getOwnPropertyNames(Person.prototype));//获取对象中的所有属性包括不可枚举的constructor
console.log(Array.prototype); */
//3、构造函数模式和原型模式的组合方式(对象设计模式的终极解决方案)
/* function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby","Court"];
}
Person.prototype = {
    constructor:Person,
    sayName:function(){
        return this.name;
    }
};
var person1 = new Person("Nichlas",19,"Software Engineer");
var person2 = new Person("Greg",27,"Doctor");
person1.friends.push("Van");
console.log(person1.friends);//["Shelby", "Court", "Van"]
console.log(person2.friends);//["Shelby", "Court"] */
//4、动态原型模式(弥补了构造函数模式中函数多次创建的问题)类似于上面构造函数中"总结"部分中的变种
/* function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    if(typeof this.sayName != "function"){
        Person.prototype.sayName = function(){
            return this.name;
        }
    }
}
var person1 = new Person("Nicholas",29,"Software Engineer");
console.log(person1.sayName()); */
//5、继承(原型链的继承:实际应用中很少使用)
/* function SuperType(){//父构造函数
    this.property = true;//构造函数模式定义属性
    this.abc = 'abc';
}
SuperType.prototype.getSuperValue = function(){//父原型对象中添加一个方法
    return this.property;
}
function SubType(){//子构造函数
    this.subproperty = false;//构造函数模式定义属性
}
SubType.prototype = new SuperType();//子原型对象继承父实例
SubType.prototype.getSubValue = function(){//字原型中添加一个方法
    return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue());//true 实现了继承
console.log(instance.abc);//abc 实现了继承
console.log(instance.getSubValue());//false 子类型中的方法(非继承)
console.log(instance instanceof Object);//true instance实例是指向Object对象的原型吗
console.log(instance instanceof SuperType);//true instance实例是指向SuperType的原型吗
console.log(instance instanceof SubType);//true instance实例是指向SubType的原型吗
console.log(Object.prototype.isPrototypeOf(instance));//true 同上
console.log(SuperType.prototype.isPrototypeOf(instance));//true 同上
console.log(SubType.prototype.isPrototypeOf(instance));//true  同上 */
//6、借用构造函数实现继承(现实中很少单独使用)
/* function SuperType(){//父构造函数
    this.color = ["red","blue","green"];
}
function SubType(){//子构造函数
    SuperType.call(this);//在该函数作用域下执行SuperType方法  (在子构造函数中借用父构造函数)
}
var instance1 = new SubType();
instance1.color.push("black");
console.log(instance1.color);//"red", "blue", "green", "black"]
var instance2 = new SubType();
console.log(instance2.color);//["red", "blue", "green"] */
//7、组合继承(最常用)缺点是调用2次SuperType构造函数优化方式为寄生组合式继承(10)
/* function SuperType(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}
SuperType.prototype.sayName = function(){
    return this.name;
}
function SubType(name,age){
    SuperType.call(this,name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    return this.age;
}
var instance1 = new SubType("Nicholas",29);
instance1.colors.push("block");//通过借用构造函数继承进行修改属性
console.log(instance1.colors);//["red", "blue", "green", "block"]  通过借用构造函数继承取值
console.log(instance1.sayName());//Nicholas 通过原型链继承方式调用到父构造函数中的原型对象中的方法
console.log(instance1.sayAge());//29 调用子构造函数中原型对象中的方法

var instance2 = new SubType("Greg",27);
console.log(instance2.colors);//["red", "blue", "green"]
console.log(instance2.sayName());//Greg 其实是又new 了一个父构造函数
console.log(instance2.sayAge());//27 */
//8、原型式继承(在没有必要兴师动众的创造构造函数的情况下使用该继承)
/* var person = {
    name:"Nicholas",
    friends:["shelby","Court","Van"]
};
var anotherPerson = Object.create(person);
console.log(anotherPerson.name);//Nicholas 对象的复制
var yetanotherPerson = Object.create(person,{//重定义属性
    name:{
        value:"Greg"
    }
})
console.log(yetanotherPerson.name);//Greg */
//9、寄生式继承(继承父类的时候可以添加自己的属性方法)函数不能复用,因此效率比较低下
/* function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
function createAnother(original){
    var clone = object(original);
    clone.sayHi = function(){
        console.log("Hi");
    };
    clone.age = 29;
    return clone;
}
var person = {
    name:"Nicholas",
    friends:["Shelby","Court","Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi();//Hi 
console.log(anotherPerson.age);//29 */
//10、寄生组合式继承(最理想的继承方式)
function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
function inheritPrototype(subType,superType){
    var prototype = object(superType.prototype);
    prototype.constructor = subType;
    subType.prototype = prototype;
}
function SuperType(name){
    this.name = name;
    this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
    return this.name;
}
function SubType(name,age){
    SuperType.call(this,name);
    this.age = age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
    return this.age;
};
var instance1 = new SubType("Nicholas",29);
console.log(instance1.name);//Nicholas
console.log(instance1.age);//29
console.log(instance1.sayName());//Nicholas
console.log(instance1.sayAge());//29