目录

基础

JavaScript的对象是一组由键-值组成的无序集合

var person = {
    name: 'Bob',
    tags: ['js', 'web', 'mobile'],
    city: 'Beijing',
    hasCar: true,
    zipcode: null
};
person.name; // 'Bob'
person.zipcode; // null

person.age; // undefined
person.age = 18; // 新增一个age属性
person.age; // 18
delete person.age; // 删除age属性

属性

是否拥有某一属性,可以用in操作符

'name' in person; // true
'grade' in person; // false
'toString' in xiaoming; // true 继承得到

判断一个属性是否是自身拥有的,而不是继承得到的

person.hasOwnProperty('name'); // true
person.hasOwnProperty('toString'); // false
var n = new Number(123); // 123,生成了新的包装类型
var b = new Boolean(true); // true,生成了新的包装类型
var s = new String('str'); // 'str',生成了新的包装类型

包装对象看上去和原来的值一模一样,显示出来也是一模一样,但他们的类型已经变为object了!所以,包装对象和原始值用 === 比较会返回 false

typeof new Number(123); // 'object'
new Number(123) === 123; // false

typeof new Boolean(true); // 'object'
new Boolean(true) === true; // false

typeof new String('str'); // 'object'
new String('str') === 'str'; // false

强制类型装换

var n = Number('123'); // 123,相当于parseInt()或parseFloat()
typeof n; // 'number'

var b = Boolean('true'); // true
typeof b; // 'boolean'

var b2 = Boolean('false'); // true! 'false'字符串转换结果为true!因为它是非空字符串!
var b3 = Boolean(''); // false

var s = String(123.45); // '123.45'
typeof s; // 'string'

方法

在一个对象中绑定函数,称为这个对象的方法

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        var y = new Date().getFullYear();
        return y - this.birth;
    }
};
xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了

this

方法内部 this 始终指向当前对象,谁(对象)调用方法,this就是谁。

function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}
var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getAge
};
xiaoming.age(); // this 是 xiaoming

// fn 虽然赋值为 xiaoming.age,但 fn 中的 this 并不会因此而绑定到 xiaoming
var fn = xiaoming.age;
fn();                  // NaN 
var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        function getAgeFromBirth() {
            var y = new Date().getFullYear();
            return y - this.birth; // 这里的 this 不指向 xiaoming
        }
        return getAgeFromBirth();
    }
};
xiaoming.age(); // Uncaught TypeError: Cannot read property 'birth' of undefined

使用变量,将 this 传入函数内部

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        var that = this; // 在方法内部一开始就捕获this
        function getAgeFromBirth() {
            var y = new Date().getFullYear();
            return y - that.birth; // 用that而不是this
        }
        return getAgeFromBirth();
    }
};
xiaoming.age(); // 25

apply

function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getAge
};

xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空

func.call(对象,参数1,参数2...)

getAge.call(xiaoming);

所有对象都是动态的,即使内置的函数,我们也可以重新指向新的函数

var count = 0;
var oldParseInt = parseInt; // 保存原函数

window.parseInt = function () {
    count += 1;
    return oldParseInt.apply(null, arguments); // 调用原函数
};

// 测试:
parseInt('10');
parseInt('20');
parseInt('30');
count; // 3

原型链上的this

var o = {f:function(){return this.a + this.b;}};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // p ---> a,p对象的原型链上的this也是指代p对象

set 与 get 函数里的this

function modulus(){
    return Math.sqrt(this.re * this.re + this.im * this.im);
}
var o = {
         re:2,
         im:8,
         get phase(){
                 return Math.atan2(this.im,this.re);
         }
}
Object.defineProperty(o,'modulus',{get:modulus,enumerable:true,configurable:true});
console.log(o.phase);
console.log(o.modulus);

构造函数里this

function MyClass(){
    this.a = 37;
}
var o = new MyClass();
console.log(o.a);  //37

function C2(){
    this.a = 37;
    return {a:38};
}
var p = new C2();
console.log(p.a); //38

call/apply方法与this

function add(c,d){
return this.a + this.b + c + d;
}
var o = {a:1,b:3};
console.log(add.call(o,5,7));    //1 + 3 + 5 + 7 = 16  call是直接传参
console.log(add.apply(o,[10,20])); //1 +3 + 10 + 20 = 34    apply是传一个参数数组进去
function bar(){
    return Object.prototype.toString.call(this);
}
console.log(bar.call(7));    //[object Number]

bind 后的this指向不会变了

function f(){
    return this.a;
}
var g = f.bind({a:"test"});
console.log(g());    //test
var o = {a:37,f:f,g:g};
console.log(o.f());    //37
console.log(o.g()); //test

利用apply(),我们还可以动态改变函数的行为。

JavaScript的所有对象都是动态的,即使内置的函数,我们也可以重新指向新的函数。

现在假定我们想统计一下代码一共调用了多少次parseInt(),可以把所有的调用都找出来,然后手动加上count += 1,不过这样做太傻了。最佳方案是用我们自己的函数替换掉默认的parseInt():

var count = 0;
var oldParseInt = parseInt; // 保存原函数

window.parseInt = function () {
    count += 1;
    return oldParseInt.apply(null, arguments); // 调用原函数
};

// 测试:
parseInt('10');
parseInt('20');
parseInt('30');
count; // 3