对象之间的继承
对象拷贝
var laoli = {
name: "laoli" ,
money: 1000000 ,
house: [ "商铺" , "住宅" ] ,
tech: function ( ) {
console. log ( "厨艺" )
}
} ;
var xiaoli = {
name: "xiaoli"
}
function extend ( parent, child) {
for ( var k in parent) {
if ( child[ k] ) {
continue ;
}
child[ k] = parent[ k] ;
}
}
extend ( laoli, xiaoli) ;
console. log ( xiaoli) ;
原型继承
封装的构造函数就是用来创建一类对象
继承指的是 类型 和 类型之间的继承
学生类型 老师类型
抽象,提取所有的公共的属性,放到一个 父类型中
当前学习阶段,没有一个专门的用来继承的方法
方法有一定缺陷,但是说明这条路是可行的
function Person ( name, age, sex) {
this . name = name;
this . age = age;
this . sex = sex;
}
function Student ( score) {
this . score = score;
}
function Teacher ( salary) {
this . salary = salary;
}
Student. prototype = new Person ( "zs" , 18 , "男" ) ;
Student. prototype. constructor = Student;
var s1 = new Student ( 89 ) ;
var s2 = new Student ( 100 ) ;
console. dir ( s1) ;
console. dir ( s2) ;
console. log ( s1. name) ;
console. log ( s1. constructor) ;
函数的 call 方法
函数本身就是一种对象,就能够有自己的属性和方法
call 方法本身是一种执行函数的方法
call 方法在调用函数的时候,有两个功能
更改函数内部的 this 指向
调用函数执行内部代码
参数: 第一个参数用来指定 this,第二个及以后,就是传的实参
function fn ( a, b) {
console. log ( this ) ;
console. log ( a + b) ;
}
var o = {
name: "zs"
}
fn. call ( o, 3 , 4 ) ;
借用构造函数继承属性
function Person ( name, age, sex) {
this . name = name;
this . age = age;
this . sex = sex;
}
function Student ( name, age, sex, score) {
Person. call ( this , name, age, sex) ;
this . score = score;
}
function Teacher ( name, age, sex, salary) {
Person. call ( this , name, age, sex) ;
this . salary = salary;
}
var s1 = new Student ( "zs" , 18 , "男" , 89 ) ;
var s2 = new Student ( "ls" , 19 , "男" , 92 ) ;
console. dir ( s1) ;
console. dir ( s2) ;
构造函数方法的继承
function Person ( name, age, sex) {
this . name = name;
this . age = age;
this . sex = sex;
}
Person. prototype. sayHi = function ( ) {
console. log ( "你好" ) ;
} ;
function Student ( name, age, sex, score) {
Person. call ( this , name, age, sex) ;
this . score = score;
}
Student. prototype = new Person ( ) ;
Student. prototype. constructor = Student;
function Teacher ( name, age, sex, salary) {
Person. call ( this , name, age, sex) ;
this . salary = salary;
}
var s1 = new Student ( "zs" , 18 , "男" , 89 ) ;
var s2 = new Student ( "ls" , 19 , "男" , 92 ) ;
console. dir ( s1) ;
console. dir ( s2) ;
s1. sayHi ( ) ;
组合继承
function Person ( name, age) {
this . name = name;
this . age = age;
}
Person. prototype. sayHi = function ( ) {
console. log ( "你好" ) ;
}
function Teacher ( name, age, salary) {
Person. call ( this , name, age) ;
this . salary = salary;
}
Teacher. prototype = new Person ( ) ;
Teacher. prototype. constructor = Teacher;
var t1 = new Teacher ( "wang" , 45 , 10000 ) ;
console. dir ( t1) ;
console. log ( t1. name) ;
t1. sayHi ( ) ;
函数声明和函数表达式
两者的区别
函数声明必须有名字
函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
函数表达式类似于变量赋值
函数表达式可以没有名字,例如匿名函数
函数表达式没有函数提升,在执行阶段创建,必须在表达式执行之后才可以调用
var fn;
if ( true ) {
fn = function ( ) {
console. log ( "fn-true" ) ;
}
} else {
fn = function ( ) {
console. log ( "fn-false" ) ;
}
}
fn ( ) ;
函数也是对象
function fn ( a, b) {
var a = 1 ;
console. log ( a + b) ;
}
var fun = new Function ( 'a' , 'b' , 'var a = "1";console.log(a+b)' ) ;
fun ( 2 , 3 ) ;
console. dir ( fun) ;
函数的调用和this
普通的函数,是通过 给函数名或者变量名添加 () 方式执行,内部的 this 默认指向 window
function fun ( ) {
console. log ( this ) ;
}
fun ( ) ;
构造函数,是通过 new 关键字进行调用,内部的 this 指向的是将来创建的实例对象
function Person ( name) {
this . name = name;
console. log ( this ) ;
}
var p1 = new Person ( "zs" ) ;
Person ( ) ;
对象中的方法,是通过对象打点调用函数,然后加小括号,内部的 this 默认指向的是调用的对象自己
var o = {
sayHi: function ( ) {
console. log ( "haha" ) ;
} ,
fn: fun
}
o. fn ( ) ;
事件函数,不需要加特殊的符号,只要事件被触发,会自动执行函数,事件函数的内部 this 指向的是事件源
document. onclick = function ( ) {
console. log ( "事件" ) ;
} ;
定时器和延时器中的函数,不需要加特殊的符号,只要执行后,在规定的时间自动执行,默认内部的 this 指向的是 window
setInterval ( function ( ) {
console. log ( "time" ) ;
} , 1000 ) ;
调用方式
非严格模式
备注
普通函数调用
window
严格模式下是 undefined
构造函数调用
实例对象
原型方法中 this 也是实例对象
对象方法调用
该方法所属对象
紧挨着的对象
事件绑定调用
绑定事件对象
定时器函数
window
call、apply、bind方法
call
call() 方法调用一个函数,其具有一个指定的this值和分别地提供的参数(参数的列表)
注意:
该方法的作用和apply() 方法类似,只有一个区别,就是call() 方法接受的是若干个参数的列表,而apply() 方法接受的是一个包含多个参数的数组
语法:
fun.call(thisArg, arg1, arg2, arg3, …)
thisArg
在 fun 函数运行时指定的 this 值
如果指定了 null 或者 undefined 则内部 this 指向 window
arg1, arg2, …
function fun ( a, b, c, d) {
console. log ( this ) ;
console. log ( a + b + c + d) ;
}
var o = {
name: "zs"
}
fun. call ( o, 1 , 2 ) ;
apply
apply() 方法调用一个函数,第一个参数是一个指定的this值,第二个参数是以一个数组(或类似数组的对象)形式提供的参数
注意:
该方法的作用和call() 方法类似,只有一个区别,就是 call() 方法接受的是若干个参数的列表,而 apply() 方法接受的是一个包含多个参数的数组
语法:
fun.apply(thisArg, [argsArray])
function fun ( a, b, c, d) {
console. log ( this ) ;
console. log ( a + b + c + d) ;
}
var o = {
name: "zs"
}
fun. apply ( o, [ 4 , 5 ] ) ;
bind
bind() 函数会创建一个新函数(称为绑定函数)新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在ECMAScript5规范中内置的call属性)
当目标函数被调用时 this 值绑定到bind()的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数
一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器。提供的this 值被忽略,同时调用时的参数被提供给模拟函数。
语法:
fun.bind(thisArg, arg1, arg2, arg3, …)
function fun ( a, b, c, d) {
console. log ( this ) ;
console. log ( a + b + c + d) ;
}
var o = {
name: "zs"
}
var fn = fun. bind ( o, 2 , 3 ) ;
console. log ( fn) ;
fn ( 6 , 7 ) ;
call 的应用
var o = {
0 : 10 ,
1 : 20 ,
2 : 30 ,
length: 3
} ;
Array. prototype. push. call ( o, 50 ) ;
console. log ( o) ;
apply 的应用
var arr = [ 1 , 3 , 4 , 6 , 8 ] ;
console. log ( 1 , 2 , 3 ) ;
console. log. apply ( console, arr) ;
bind 的应用
var o = {
name: "zs" ,
age: 18 ,
s: function ( ) {
setInterval ( function ( ) {
console. log ( this . age) ;
} . bind ( this ) , 1000 ) ;
}
}
document. onclick = function ( ) {
console. log ( this ) ;
} . bind ( o) ;
函数的其他成员
function fun ( ) {
console. log ( 1 ) ;
}
console. dir ( fun) ;
function fn ( a, b) {
console. log ( arguments) ;
console. log ( fn. arguments) ;
console. log ( fn. caller) ;
console. log ( fn. length) ;
console. log ( fn. name) ;
}
灵活使用 arguments 类数组对象,可以记录所有的实参
function max ( ) {
var nowMax = arguments[ 0 ] ;
for ( var i = 1 ; i < arguments. length; i++ ) {
if ( arguments[ i] > nowMax) {
nowMax = arguments[ i] ;
}
}
return nowMax;
}
console. log ( max ( 1 , 4 , 7 , 9 ) ) ;
高阶函数
function eat ( fn) {
console. log ( "吃晚饭" ) ;
fn ( ) ;
}
eat ( function ( ) {
console. log ( "看电影" ) ;
} ) ;
function outer ( n) {
return function inner ( m) {
console. log ( m + n) ;
}
}
var fun = outer ( 100 ) ;
fun ( 3 ) ;
fun ( 13 ) ;
fun ( 23 ) ;
var fun1 = outer ( 1000 ) ;
fun1 ( 3 ) ;
闭包
什么是闭包
一个函数和对其轴为状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围)这样的组合就是闭包(closure)也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在JavaXcript中,每当创建一个函数,闭包就会在函数创建的同时被创建出来
闭包
函数定义时天生就能记住自己生成的作用域环境和函数自己,将它们形成一个密闭的环境,这就是闭包。不论函数以任何方式在任何地方进行调用,都会回到自己定义时的密闭环境进行执行。
观察闭包
从广义上来说,定义在全局的函数也是一个闭包,只是我们没办法将这样的函数拿到更外面的作用域进行调用,从而观察闭包的特点。
闭包是天生存在的,不需要额外的结构,但是我们为了方便观察闭包的特点,需要利用一些特殊结构将一个父函数内部的子函数拿到父函数外部进行调用,从而观察闭包的存在
体会闭包
function outer ( ) {
var a = 10 ;
function inner ( ) {
console. log ( a) ;
}
return inner;
}
var inn = outer ( ) ;
inn ( ) ;
闭包的理解和应用
将一个内部函数拿到父函数的外面,观察是否还能调用父函数内部的变量
形成闭包环境中的变量不是一成不变的,可以被更改
function outer ( ) {
var a = 10 ;
function inner ( ) {
console. log ( a++ ) ;
}
return inner;
}
var inn = outer ( ) ;
inn ( ) ;
inn ( ) ;
闭包的用途
可以在函数外部读取函数内部成员
让函数内成员始终活在内存中
闭包的问题
var arr = [ ] ;
for ( var i = 0 ; i <= 10 ; i++ ) {
( function ( i) {
arr[ i] = function ( ) {
console. log ( i) ;
} ;
} ) ( i) ;
}
arr[ 0 ] ( ) ;
arr[ 1 ] ( ) ;
arr[ 2 ] ( ) ;