显示绑定:css
JavaScript 中的“全部”函数都有一些有用的特性(这和它们的 [[Prototype]] 有关——以后咱们会详细介绍原型),能够用来解决这个问题。具体点说,可使用函数的 call(..) 和apply(..) 方法。严格来讲,JavaScript 的宿主环境有时会提供一些很是特殊的函数,它们并无这两个方法。可是这样的函数很是罕见,JavaScript 提供的绝大多数函数以及你自己建立的全部函数均可以使用 call(..) 和 apply(..) 方法。数组
这两个方法是如何工做的呢?它们的第一个参数是一个对象,是给 this 准备的,接着在调用函数时将其绑定到 this。由于你能够直接指定 this 的绑定对象,所以咱们称之为显式绑定。数据结构
思考下面的代码:app
function foo() { console.log( this.a ); } var obj = { a:2 }; foo.call( obj ); // 2
经过 foo.call(..),咱们能够在调用 foo 时强制把它的 this 绑定到 obj 上。ide
用call来实现继承函数
var test = { name: 'He', age: 25 }
var test2 = { write: function () { console.log(this.name) }, writeAge: function () { console.log(this.age) } }
test2.write.call(test) //He test2.writeAge.call(test) //25 test2.write(); //undefined test2.writeAge(); //undefined
1. 函数是否在 new 中调用(new 绑定)?若是是的话 this 绑定的是新建立的对象。
var bar = new foo()ui
2. 函数是否经过 call、apply(显式绑定)或者硬绑定调用?若是是的话,this 绑定的是指定的对象。
var bar = foo.call(obj2)this
3. 函数是否在某个上下文对象中调用(隐式绑定)?若是是的话,this 绑定的是那个上下文对象。
var bar = obj1.foo()spa
4. 若是都不是的话,使用默认绑定。若是在严格模式下,就绑定到 undefined,不然绑定到全局对象。
var bar = foo() prototype
var myobject={ //... } myobject.a //undefined
访问myobject对象的a属性,返回的值为 undefined,可是这个值有多是属性中存储的 undefined,也多是属性不存在返回的 undefined,那么如何区分这两种状况呢?
咱们能够在不访问属性值的状况下判断对象中是否存在这个属性:
var myObject = { a:2 };
("a" in myObject); // true ("b" in myObject); // false
myObject.hasOwnProperty( "a" ); // true myObject.hasOwnProperty( "b" ); // false
in 操做符会检查属性是否在对象及其 [[Prototype]] 原型链中,hasOwnProperty(..) 只会检查属性是否在 myObject 对象中,不会检查 [[Prototype]] 链。
看起来in像是检测容器内是否存在某个值,可是实际上检查的是某个属性名是否存在。
4 in [2,4,6] //false
数组[2,4,6]的属性名是0,1,2 由于检测的是属性名, 因此会输出false
//// 数组有内置的 @@iterator,所以 for..of 能够直接应用在数组上。咱们使用内置的 @@iterator 来手动遍历数组,看看它是怎么工做的 var myArr = [1, 2, 3]; var it = myArr[Symbol.iterator](); it.next(); // { value:1, done:false } it.next(); // { value:2, done:false } it.next(); // { value:3, done:false } it.next(); // { done:true }
如你所见,调用迭代器的 next() 方法会返回形式为 { value: .. , done: .. } 的值,value 是当前的遍历值,done 是一个布尔值,表示是否还有能够遍历的值。
注意,和值“3”一块儿返回的是 done:false,乍一看好像很奇怪,你必须再调用一次next() 才能获得 done:true,从而肯定完成遍历。这个机制和 ES6 中发生器函数的语义相关,不过已经超出了咱们的讨论范围。
和数组不一样,普通的对象没有内置的 @@iterator,因此没法自动完成 for..of 遍历。之因此要这样作,有许多很是复杂的缘由,不过简单来讲,这样作是为了不影响将来的对象类型。
固然,你能够给任何想遍历的对象定义 @@iterator,举例来讲:
var myObject = { a: 2, b: 3 }; Object.defineProperty( myObject, Symbol.iterator, { enumerable: false, writable: false, configurable: true, value: function() { var o = this; var idx = 0; var ks = Object.keys( o ); return { next: function() { return { value: o[ks[idx++]], done: (idx > ks.length) }; } }; } } ); // 手动遍历 myObject var it = myObject[Symbol.iterator](); it.next(); // { value:2, done:false } it.next(); // { value:3, done:false } it.next(); // { value:undefined, done:true } // 用 for..of 遍历 myObject for (var v of myObject) { console.log( v ); } // 2 // 3
咱们使用 Object.defineProperty(..) 定义了咱们本身的 @@iterator(主要是为了让它不可枚举),不过注意,咱们把符号看成可计算属性名(本章以前有介绍)。此外,也能够直接在定义对象时进行声明,好比 var myObject = { a:2, b:3, [Symbol.iterator]: function() { /* .. */ } }。
for..of 循环每次调用 myObject 迭代器对象的 next() 方法时,内部的指针都会向前移动并返回对象属性列表的下一个值(再次提醒,须要注意遍历对象属性 / 值时的顺序)。
一、对象就是键 / 值对的集合。能够经过 .propName 或者 ["propName"] 语法来获取属性值。访问属性时,引擎实际上会调用内部的默认 [[Get]] 操做(在设置属性值时是 [[Put]]),[[Get]] 操做会检查对象自己是否包含这个属性,若是没找到的话还会查找 [[Prototype]]链(参见第 5 章)。
二、属性的特性能够经过属性描述符来控制,好比 writable 和 configurable。此外,可使用Object.preventExtensions(..)、Object.seal(..) 和 Object.freeze(..) 来设置对象(及其属性)的不可变性级别
三、属性不必定包含值——它们多是具有 getter/setter 的“访问描述符”。此外,属性能够是可枚举或者不可枚举的,这决定了它们是否会出如今 for..in 循环中。
四、你可使用 ES6 的 for..of 语法来遍历数据结构(数组、对象,等等)中的值,for..of会寻找内置或者自定义的 @@iterator 对象并调用它的 next() 方法来遍历数据值。
var foo2 = { name: 'He', sayName: function () { console.log(this.name) } } var foo2_n = Object.create(foo2); foo2_n.sayName();
Task = { setId: function (id) { this.id = id }, outputId: function () { console.log(this.id) } } // 让 XYZ 委托 Task xyz = Object.create(Task); xyz.prepareTask = function (id, label) { this.setId(id); this.label = label } xyz.outputTaskDetails = function () { this.outputId(); console.log(this.label) } // xyz.prepareTask(1,'委托')
Foo = { init: function (who) { this.me = who }, identify: function () { return 'I am ' + this.me + '.' } } Bar = Object.create(Foo); Bar.speak = function () { console.log("Hello, " + this.identify()) } var a1 = Object.create(Bar); a1.init('A1'); var b1 = Object.create(Bar); b1.init('B1'); a1.speak(); b1.speak();
// 父类 function widget(width, height) { this.width = width || 50; this.height = height || 50; this.$elem = null; } widget.prototype.render = function ($where) { if (this.$elem) { this.$elem.css({ width: this.width + 'px', height: this.height + 'px' }).appendTo($where); } } //子类 function Button(width, height, label) { widget.call(this, width, height); this.label = label || "default"; this.$elem = $('<button>').text(this.label); } // 让Button继承widget Button.prototype = Object.create(widget); // 重写render方法 Button.prototype.render = function ($where) { widget.prototype.render.call(this, $where); this.$elem.click(this.onClick.bind(this)); } Button.prototype.onClick = function (evt) { console.log("Button " + this.label + " clicked!") } $(function () { var $body = $(document.body); var button1 = new Button(70, 70, '按钮1'); var button2 = new Button(90, 90, '按钮2'); button1.render($body); button2.render($body); })
var widget2={ init:function(width,height){ this.width=width||50; this.height=height||50; this.$elm=null; }, insert:function($where){ if(this.$elm){ this.$elm.css({ width:this.width+'px', height:this.height+'px', marginLeft:'30px' }).appendTo($where) } } } var Button2=Object.create(widget2); Button2.setup=function(width,height,label){ this.init(width,height); this.label=label||"default"; this.$elm=$("<button>").text(this.label); } Button2.build=function($where){ this.insert($where); this.$elm.click(this.onClick.bind(this)); } Button2.onClick=function(){ console.log("Button "+this.label+" clicked"); } $(document).ready(function(){ var body=$(document.body); var button1=Object.create(Button2); var button2=Object.create(Button2); button1.setup(80,80,'取消') button1.build(body) button2.setup(80,80,'肯定!'); button2.build(body); })
ES5 规范 9.2 节中定义了抽象操做 ToBoolean,列举了布尔强制类型转换全部可能出现的
结果。
如下这些是假值:
• undefined
• null
• false
• +0、-0 和 NaN
• ""
假值的布尔强制类型转换结果为 false。
一元运算符 ! 显式地将值强制类型转换为布尔值。可是它同时还将真值反转为假值(或者将假值反转为真值)。因此显式强制类型转换为布尔值最经常使用的方法是 !!,由于第二个 ! 会将结果反转回原值
var a = "0"; var b = []; var c = {}; var d = ""; var e = 0; var f = null; var g; !!a; // true !!b; // true !!c; // true !!d; // false !!e; // false !!f; // false !!g; // false
优先级
var a = 42; var b = "foo"; var c = false; var d = a && b || c ? c || b ? a : c && b : a; d; // 42
运算符优先级 && > || > ? :
利用优先级将代码分解:
((a && b) || c) ? ((c || b) ? a : (c && b)) : a
如今来逐一执行
(1) (a && b) 结果为 "foo"。
(2) "foo" || c 结果为 "foo"。
(3) 第一个 ? 中,"foo" 为真值。
(4) (c || b) 结果为 "foo"。
(5) 第二个 ? 中,"foo" 为真值。
(6) a 的值为 42。
所以,最后结果为 42。
typeof 适合基本类型和函数对象的判断 不适用array等特殊类型
typeof 100;//number typeof true;//boolean typeof function(){};//function typeof (undefined);//undefined typeof new Object;//Object typeof [1,2];//Object typeof NaN;//number typeof null;//Object
instanceof 经常使用于判断对象类型,它是基于原型链去判断的操做符
(obj instanceof Object)
instanceof它但愿左操做数(obj)是一个对象,若是不是对象,是基本数据类型(好比 string,number),直接返回false。
它但愿右操做数必须是一个函数对象或者函数构造器,若是不是会抛出异常Type Error。
instanceof大概原理:判断左操做数对象的原型链上是否有右构造函数的prototype属性
[1,2] instanceof Array;//true new Object() instanceof Array; //false