new演算子の内部仕組み
new演算子はオブジェクトをインスタンス化し、新しいオブジェクトを返します。コンストラクタ関数内のthisはインスタンスに参照されます。
new演算子の内部プロトタイプ
- var p1 = {} で新規オブジェクトを作成し、メモリ空間を確保します。
- Person.call(p1) を使用して、Person関数内のthisをp1に変更します。
- p1.__proto__ == Person.prototype により、p1のポインタがコンストラクタのプロトタイプを指し、属性とメソッドを継承します。
プロトタイプの参照は変更可能か
function Animal() { }
Animal.prototype.name = "犬"
function Dog() { }
Dog.prototype.color = "黄色"
Dog.prototype = new Animal()
console.log(Animal.prototype.constructor == Animal)//true
console.log(Dog.prototype.constructor == Dog);//false
console.log(Dog.prototype.constructor == Animal);//true
3つの継承方法
プロトタイプ継承、コンストラクタによる継承、コピーによる継承があります。
プロトタイプ継承
子クラスが親クラスの属性とメソッドを継承する方法です。
//人用のコンストラクタ
function Person(name){
this.name = name
this.work = function () {
console.log(this.name + "仕事好き")
}
}
//人のプロトタイプ
Person.prototype.age = 26
Person.prototype.playCode = function (){
console.log(this.name + "コード書くのが好き")
}
//学生用のコンストラクタ
function Student(sex) {
this.sex = sex
this.eat = function () {
console.log(this.name + "お菓子食べる")
}
}
Student.prototype = new Person("太郎")
//学生のプロトタイプ
Student.prototype.className = "Web開発"
Student.prototype.learn = function () {
console.log(this.name + "学ぶのが好き")
}
var stu = new Student("男")
console.log(stu.sex)//男
stu.eat()//太郎お菓子食べる
console.log(stu.className)//Web開発
stu.learn()//太郎学ぶのが好き
console.log(stu.name)//太郎
stu.work()//太郎仕事好き
console.log(stu.age)//26
stu.playCode()//太郎コード書くのが好き
コンストラクタによる継承(callメソッド)
//人用のコンストラクタ
function Person(name, age) {
this.name = name
this.age = age
this.work = function () {
console.log(this.name + "仕事好き")
}
}
//人のプロトタイプ
Person.prototype.sex = "男"
Person.prototype.basketball = function () {
console.log(this.name + "バスケする")
}
//学生用のコンストラクタ
function Student(score, x, y) {
this.score = score
this.study = function () {
console.log(this.name + "勉強する")
}
Person.call(this, x, y)
}
Student.prototype = new Person()
//学生のプロトタイプ
Student.prototype.className = "Web31"
Student.prototype.playCode = function () {
console.log(this.name + "コード書く")
}
var stu = new Student(100, "花子", 18)
console.log(stu.score)
stu.study()
console.log(stu.className)
stu.playCode()
console.log(stu.name)
console.log(stu.age)
stu.work()
console.log(stu.sex)
stu.basketball()
コピーによる継承
//人用のコンストラクタ
function Person(name) {
this.name = name
this.work = function () {
console.log(this.name + "仕事好き")
}
}
//人のプロトタイプ
Person.prototype.age = 26
Person.prototype.code = function () {
console.log(this.name + "コード書く")
}
//学生用のコンストラクタ
function Student(sex) {
this.sex = sex
this.eat = function () {
console.log(this.name + "お菓子食べる")
}
}
//学生のプロトタイプ
Student.prototype.className = "Web開発"
Student.prototype.study = function () {
console.log(this.name + "学ぶ")
}
var stu = new Student("男")
var p = new Person("太郎")
for (x in p) {
stu[x] = p[x]
}
console.log(stu.sex)//男
stu.eat()//太郎お菓子食べる
console.log(stu.className)//Web開発
stu.study()//太郎学ぶ
console.log(p.name)//太郎
p.work()//太郎仕事好き
console.log(p.age)//26
p.code()//太郎コード書く
スタックとヒープ
JavaScriptでは変数はスタックとヒープに保存されます。
- スタック:基本型と参照型のアドレスを格納し、自動的にメモリが解放されます。
- ヒープ:動的にメモリが割り当てられ、解放されません。参照型の値を格納します。
深コピーと浅コピー
基本型はスタックに保存され、参照型はヒープに保存されます。
浅コピー
元の値に影響を与えます。
配列の浅コピー
var arr1 = [1,2,3]
var arr2 = arr1
console.log(arr2)//[1,2,3]
arr2.push(4)
console.log(arr2)//[1,2,3,4]
console.log(arr1)//[1,2,3,4]
オブジェクトの浅コピー
var obj1 = {
name: "太郎",
age: 18,
}
var obj2 = obj1
obj2.sex = "男"
console.log(obj2);//{name: '太郎', age: 18, sex: '男'}
console.log(obj1);//{name: '太郎', age: 18, sex: '男'}
深コピー
元の値に影響を与えません。
配列の深コピー
//1、forループ
var arr3 = [1,2,3]
var arr4 = []
for(var i = 0;i < arr3.length; i++){
arr4.push(arr3[i])
}
console.log(arr4)//[1,2,3]
arr4.push(4)
console.log(arr4)//[1,2,3,4]
console.log(arr3)//[1,2,3]
//2、slice
var arr1 = [1,2,3]
var arr2 = arr1.slice(0)
arr2.push(5)
console.log(arr2);//[1,2,3,5]
console.log(arr1);//[1,2,3]
//3、concat()
var arr1 = [1,2,3]
var arr2 = arr1.concat()
console.log(arr2)//[1,2,3]
arr2.push(6)
console.log(arr2)//[1,2,3,6]
console.log(arr1)//[1,2,3]
オブジェクトの深コピー
//1、forループ
var obj1 = {
x: 1,
y: 2,
}
var obj2 ={ }
for(x in obj1) {
obj2[x] = obj1[x]
}
console.log(obj2);//{x: 1, y: 2}
obj2.z = 3
console.log(obj2);//{x: 1, y: 2, z: 3}
console.log(obj1)//{x: 1, y: 2}
練習問題
var fun = function () { }
fun.prototype = {
name: 'ピーター',
age: 25
}
var a = new fun();
var b = new fun();
console.log(a.name, b.name);//ピーター ピーター
fun.prototype.name = 'ジャック';
console.log(a.name, b.name);//ジャック ジャック
fun.prototype = {};
fun.prototype.name = 'トム';console.log(a.name, b.name);//ジャック ジャック
b.constructor.prototype.name = 'キティ';
function A() {}
function B(a) {
this.a = a;
}
function C(a) {
if (a) {
this.a = a;
}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a);//1
console.log(new B().a);//undefined
console.log(new C(2).a);//2
console.log(new C(0).a);//1
//メソッドがない場合はエラーになります
var F = function () {
}
Object.prototype.a = function () {
console.log('a()')
}
Function.prototype.b = function () {
console.log('b()')
}
var f = new F()
f.a()//a() コンストラクタにはないが、Objectにある
f.b()//エラー コンストラクタやプロトタイプにない
F.a()//a() プロトタイプがある
F.b()//b()
var P = function () { }
P.prototype.aaa = function () {
alert("ddd")
}
var a = new P();
var num = 10;//var num = new Number(10)
console.log(P.__proto__ == Function.prototype);//true
console.log(P == a.constructor);//true
console.log(a.__proto__ == P.prototype);//true
console.log(P.prototype.__proto__ == Object.prototype);//true
console.log(a.prototype);//undefined
console.log(Number.__proto__ == Function.prototype);//true
console.log(Number.prototype == num.__proto__);//true
console.log(Function.__proto__ == Function.prototype);//true
console.log(Function.prototype == P.__proto__);//true
console.log(Function.prototype.__proto__ == Object.prototype);//true
console.log(Object.__proto__ == Function.prototype);//true
console.log(Object.prototype.__proto__ == null);//true