@@ -78,28 +78,120 @@ class p5 {
7878 this . _updateWindowSize ( ) ;
7979
8080 const bindGlobal = property => {
81- Object . defineProperty ( window , property , {
82- configurable : true ,
83- enumerable : true ,
84- get : ( ) => {
85- if ( typeof this [ property ] === 'function' ) {
86- return this [ property ] . bind ( this ) ;
87- } else {
88- return this [ property ] ;
89- }
90- } ,
91- set : newValue => {
81+ if ( property === 'constructor' ) return ;
82+
83+ // Common setter for all property types
84+ const createSetter = ( ) => newValue => {
85+ Object . defineProperty ( window , property , {
86+ configurable : true ,
87+ enumerable : true ,
88+ value : newValue ,
89+ writable : true
90+ } ) ;
91+ if ( ! p5 . disableFriendlyErrors ) {
92+ console . log ( `You just changed the value of "${ property } ", which was a p5 global value. This could cause problems later if you're not careful.` ) ;
93+ }
94+ } ;
95+
96+ // Check if this property has a getter on the instance or prototype
97+ const instanceDescriptor = Object . getOwnPropertyDescriptor ( this , property ) ;
98+ const prototypeDescriptor = Object . getOwnPropertyDescriptor ( p5 . prototype , property ) ;
99+ const hasGetter = ( instanceDescriptor && instanceDescriptor . get ) ||
100+ ( prototypeDescriptor && prototypeDescriptor . get ) ;
101+
102+ // Only check if it's a function if it doesn't have a getter
103+ // to avoid actually evaluating getters before things like the
104+ // renderer are fully constructed
105+ let isPrototypeFunction = false ;
106+ let isConstant = false ;
107+ let constantValue ;
108+
109+ if ( ! hasGetter ) {
110+ const prototypeValue = p5 . prototype [ property ] ;
111+ isPrototypeFunction = typeof prototypeValue === 'function' ;
112+
113+ // Check if this is a true constant from the constants module
114+ if ( ! isPrototypeFunction && constants [ property ] !== undefined ) {
115+ isConstant = true ;
116+ constantValue = prototypeValue ;
117+ }
118+ }
119+
120+ if ( isPrototypeFunction ) {
121+ // For regular functions, cache the bound function
122+ const boundFunction = p5 . prototype [ property ] . bind ( this ) ;
123+ if ( p5 . disableFriendlyErrors ) {
92124 Object . defineProperty ( window , property , {
93125 configurable : true ,
94126 enumerable : true ,
95- value : newValue ,
96- writable : true
127+ value : boundFunction ,
128+ } ) ;
129+ } else {
130+ Object . defineProperty ( window , property , {
131+ configurable : true ,
132+ enumerable : true ,
133+ get ( ) {
134+ return boundFunction ;
135+ } ,
136+ set : createSetter ( )
97137 } ) ;
98- if ( ! p5 . disableFriendlyErrors ) {
99- console . log ( `You just changed the value of "${ property } ", which was a p5 global value. This could cause problems later if you're not careful.` ) ;
100- }
101138 }
102- } ) ;
139+ } else if ( isConstant ) {
140+ // For constants, cache the value directly
141+ if ( p5 . disableFriendlyErrors ) {
142+ Object . defineProperty ( window , property , {
143+ configurable : true ,
144+ enumerable : true ,
145+ value : constantValue ,
146+ } ) ;
147+ } else {
148+ Object . defineProperty ( window , property , {
149+ configurable : true ,
150+ enumerable : true ,
151+ get ( ) {
152+ return constantValue ;
153+ } ,
154+ set : createSetter ( )
155+ } ) ;
156+ }
157+ } else if ( hasGetter || ! isPrototypeFunction ) {
158+ // For properties with getters or non-function properties, use lazy optimization
159+ // On first access, determine the type and optimize subsequent accesses
160+ let lastFunction = null ;
161+ let boundFunction = null ;
162+ let isFunction = null ; // null = unknown, true = function, false = not function
163+
164+ Object . defineProperty ( window , property , {
165+ configurable : true ,
166+ enumerable : true ,
167+ get : ( ) => {
168+ const currentValue = this [ property ] ;
169+
170+ if ( isFunction === null ) {
171+ // First access - determine type and optimize
172+ isFunction = typeof currentValue === 'function' ;
173+ if ( isFunction ) {
174+ lastFunction = currentValue ;
175+ boundFunction = currentValue . bind ( this ) ;
176+ return boundFunction ;
177+ } else {
178+ return currentValue ;
179+ }
180+ } else if ( isFunction ) {
181+ // Optimized function path - only rebind if function changed
182+ if ( currentValue !== lastFunction ) {
183+ lastFunction = currentValue ;
184+ boundFunction = currentValue . bind ( this ) ;
185+ }
186+ return boundFunction ;
187+ } else {
188+ // Optimized non-function path
189+ return currentValue ;
190+ }
191+ } ,
192+ set : createSetter ( )
193+ } ) ;
194+ }
103195 } ;
104196 // If the user has created a global setup or draw function,
105197 // assume "global" mode and make everything global (i.e. on the window)
0 commit comments